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.