open class Animal(val name: String)
class Dog(name: String) : Animal(name)
class Box<T>(private val item: T) {
fun get(): T {
return item
}
}
fun main() {
val dogBox: Box<Dog> = Box(Dog("Buddy"))
val animalBox: Box<Animal> = dogBox // This would NOT compile
}
Which is expected given that generics are invariant (we could solve this by using "out" - allowing covariance), but if we change the main's method body to
val animalBox: Box<Animal> = Box(Dog("Buddy")) // This compiles
I don't understand why this happens nor how this is internally processed. The RHS doesn't turn out to be Box<Dog>
? Does the generic matches Animal after all?
When you call the Box
constructor, type inference is performed to determine the type parameter of Box
. It ends up deciding that the type parameter of Box
is Animal
, i.e. you are doing:
val animalBox: Box<Animal> = Box<Animal>(Dog("Buddy"))
The Box<Animal>
constructor takes an instance of Animal
. Dog
can be converted to Animal
, so there is no problem there at all.
The key idea here is that type inference is bidirectional. From the language spec,
Type inference in Kotlin is bidirectional; meaning the types of expressions may be derived not only from their arguments, but from their usage as well. Note that, albeit bidirectional, this process is still local, meaning it processes one statement at a time, strictly in the order of their appearance in a scope; e.g., the type of property in statement S1 that goes before statement S2 cannot be inferred based on how S1 is used in S2.
Type inference not only takes into account the argument Dog("Buddy")
, it also considers the context in which the expression occurs - the right hand side of the assignment needs to be of type Box<Animal>
.