Upper Bounds
A simple declaration of T could be any type. Sometimes, we need to limit it to only be certain types. The simplest way to do that is to declare T a bit like a class or interface, using a colon and a type to declare what T must inherit from:
open class Thingy
open class Animal : Thingy()
class Frog : Animal()
class Axolotl : Animal()
interface Transport<T : Thingy> {
  abstract fun getPassenger(): T
}
data class Van(val animal: Animal) : Transport<Animal> {
  override fun getPassenger() = animal
}
fun main() {
  val kermit = Frog()
  val critterCarrier = Van(kermit)
  println(critterCarrier.getPassenger()::class)
}
Now Transport cannot transport anything — it is limited to instances of Thingy. Since an Animal is a Thingy, we can still transport animals. But we cannot create a Transport of String, because a String does not extend from Thingy. If we try anyway:
open class Thingy
interface Transport<T : Thingy> {
  abstract fun getPassenger(): T
}
data class StringVehicle(val value: String) : Transport<String> {
  override fun getPassenger() = value
}
…we wind up with a compile error:
error: type argument is not within its bounds: should be subtype of 'Test.Thingy'
data class StringVehicle(val value: String) : Transport<String> {
                                                        ^
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.