Applying Generics to Classes and Interfaces
You may want to support generics in your own classes and interfaces. This works a lot like how it does in Java, where you use T notation to indicate where you accept a varying type.
Use in Class Declaration
For example, you can create a class that can work with a particular type:
open class Animal
class Frog : Animal()
class Axolotl : Animal()
data class Transport<T>(var passenger: T)
fun main() {
val kermit = Frog()
val critterCarrier = Transport(kermit)
println(critterCarrier.passenger::class)
}
Transport can handle an object of any type, but a particular instance of Transport can only handle objects of a single type (or subtypes, where applicable). When we pass a Frog instance into the Transport constructor, we lock the resulting Transport to transporting Frog objects. If we attempt to reassign passenger with another type:
critterCarrier.passenger = "This is not an Animal"
…we get a compile error:
error: type mismatch: inferred type is String but Test.Frog was expected
critterCarrier.passenger = "This is not an Animal"
^
What the Transport can support will be based on the type of the variable or property handed to it. So, we can give a Transport a bit more flexibility by passing in an Animal, instead of a Frog, to it:
open class Animal
class Frog : Animal()
class Axolotl : Animal()
data class Transport<T>(var passenger: T)
fun main() {
val kermit: Animal = Frog()
val critterCarrier = Transport(kermit)
println(critterCarrier.passenger::class)
critterCarrier.passenger = Axolotl()
println(critterCarrier.passenger::class)
}
This works because now kermit is typed as an Animal, so critterCarrier is a Transport of Animal, so we can replace our Frog with an Axoltl if desired.
Use with Supertypes
We could also create a subtype that declares support for a specific type:
open class Animal
class Frog : Animal()
class Axolotl : Animal()
interface Transport<T> {
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)
}
Here, while Transport can handle any type, Van is an implementation of Transport that is restricted to transporting Animal objects.
Note that we can use the T type placeholder for properties, parameters, and — in the case of getPassenger() — return values.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.