Consider the following example: I have an abstract class for Animal, and every animal has a mouth, but because every animal's mouth is different, the mouth class is also abstract:
abstract class Animal {
var numberLegs: Int = 4
var mouth: Mouth? = null
}
abstract class Mouth {
abstract fun makeSound()
}
I can now create a Dog and a DogMouth:
class Dog: Animal() {
override var mouth: Mouth = DogMouth()
}
class DogMouth: Mouth() {
override fun makeSound() {
println("Bark!")
}
}
But this allows me to also assign other types of mouths to the dog, which I don't want, e.g.:
class CatMouth: Mouth() {
override fun makeSound() {
println("Meow!")
}
}
fun main() {
val dog = Dog()
dog.mouth.makeSound() // will print "Bark!"
dog.mouth = CatMouth() // I don't want this to work
dog.mouth.makeSound() // will print "Meow!"
}
And setting override var mouth: DogMouth = DogMouth()
doesn't work.
How can I make sure that Dogs only have DogMouths (and other dog body parts)?
Similar problems are addressed here and here. The solution is to use a generic parameter:
abstract class Animal<MouthType: Mouth> {
var numberLegs: Int = 4
abstract var mouth: MouthType
}
class Dog: Animal<DogMouth>() {
override var mouth: DogMouth = DogMouth()
}
This makes dog.mouth = CatMouth()
fail with a type mismatch.
With more body parts extra generics need to be added:
abstract class Animal<MouthType: Mouth, EarType: Ear, TailType: Tail> {
var numberLegs: Int = 4
abstract var mouth: MouthType
abstract var ear: EarType
abstract var tail: TailType
}