I'd like to know the background of why a generic supertype parameter does not allow referencing to subtype objects.
abstract class Pet()
class Cat: Pet()
interface Retailer<T> {
fun sell(): T
}
class CatRetailer: Retailer<Cat> {
override fun sell(): Cat {
println("Sell Cat")
return Cat()
}
}
// Type MismatchError prior to compilation
val steveIrwinTheAnimalEnslaver: Retailer<Pet> = CatRetailer()
The variable definition results in a type mismatch error where the compiler expects a type of Retailer<Pet>
.
However, Pet
is a supertype of Cat
. Why doesn't polymorphism work like below?
open class SuperClassName() {}
class SubClassName : SuperClassName()
var variableName: SuperClassName = SubClassName()
Pet
is a supertype of Cat
, but Retailer<Pet>
is not a supertype of Retailer<Cat>
. To see why imagine you added a method:
abstract class Pet()
class Cat: Pet()
class Dog: Pet()
interface Retailer<T> {
fun sell(): T
fun buy(x: T): Unit
}
// only really knows how to buy cats
val steveIrwinTheAnimalEnslaver: Retailer<Pet> = CatRetailer()
// legal for any Retailer<Pet>
steveIrwinTheAnimalEnslaver.buy(Dog())
In this case your options are:
Use Retailer<out Pet>
, which disallows calling members like buy
which "consume" T
. You can think of it as Retailer<any subtype of Pet>
, and you won't be much wrong. There is also the dual Retailer<in Pet>
which allows calling buy
but not sell
.
Declare interface Retailer<out T>
which disallows declaring buy
and means "if A
is a subtype of B
, then Retailer<A>
is a subtype of Retailer<B>
.