I'm looking for an example that can cause a problem when using out in class declaration and the class has a method that get the parameter type as argument.
Also I'm looking for an example that can cause a problem when using in in class declaration and the parameter type is a var member of the class? I think that i will be able to understand the rules only by examples
Suppose these are the classes we are working with:
open class Animal
class Cat: Animal() {
fun meow() = println("meow")
}
If we create a class like this with covariant out
type and the compiler allowed us to use the type as a function parameter:
class Foo<out T: Animal> {
private var animal: T? = null
fun consumeValue(x: T) { // NOT ALLOWED
animal = x
}
fun produceValue(): T? {
return animal
}
}
Then if you do this, it will be lead to an impossible situation where we are trying to call meow
on an Animal that doesn't have a meow
function:
val catConsumer = Foo<Cat>()
val animalConsumer: Foo<Animal> = catConsumer // upcasting is valid for covariant type
animalConsumer.consumeValue(Animal())
catConsumer.produceValue()?.meow() // can't call `meow` on plain Animal
And if we create a class like this with contravariant in
type and the compiler allowed us to use the type as a return value:
class Bar<in T: Animal>(private val library: List<T>) {
fun produceValue(): T { // NOT ALLOWED
return library.random()
}
}
Then if you do this, it will lead to the compiler impossibly casting a return type to a subtype.
val animalProducer: Bar<Animal> = Bar(List(5) { Animal() })
val catProducer: Bar<Cat> = animalProducer // downcasting is valid for contravariant type
catProducer.produceValue().meow() // can't call `meow` on plain Animal
A property has a getter which is just like a function that returns a value, and a var
property additionally has a setter, which is just like a function that takes a parameter. So val
properties are not compatible with contravariance (in
) and var
properties are not compatible with contravariance or covariance (out
). Private properties aren't encumbered by these restrictions because within the class's inner workings, the type is invariant. All the class can know about its own type is its bounds. Variance just affects how the class can be cast (viewed) by the outside world.
So an example with val
is enough to show why any property is incompatible with contravariance. You could replace val
with var
below and it would be no different.
class Bar<in T: Animal>(
val animal: T // NOT ALLOWED
)
val animalProducer: Bar<Animal> = Bar(Animal())
val catProducer: Bar<Cat> = animalProducer // downcasting is valid for contravariant type
catProducer.animal.meow() // can't call `meow` on plain Animal