Here's a code in which I'm having hard time understanding why the first compiles and second doesn't?
class Test11<T : Number> {
lateinit var test: MutableList<out T>.() -> Unit
}
fun main() {
val test: Test11<Int> = Test11<Int>()
val test2: Test11<out Number> = test
test.test.invoke(MutableList(3) { 55 }) // First
test2.test.invoke(MutableList(3) { 55 }) // Second
}
The second says MutableList<Nothing>
was expected.
So basically in first case, T => Int
so out out T => out Int => out Number
maybe. In second case, T => out Number
so anything which is subclass of Number, then still out T => out Number
right?
I'm not able to understand why doesn't it work by that logic...
The MutableList is a function parameter. You'd have the exact same issue with:
class Test11<T : Number> {
fun test(list: MutableList<out T>) {
}
}
fun main() {
val test: Test11<Number> = Test11<Number>()
val test2: Test11<out Number> = test
test.test(MutableList(3) { 55 }) // First
test2.test(MutableList(3) { 55 }) // Second
}
A covariant type by definition prevents functions where the type is a parameter from being called, but this also logically extends to nested covariance of the same type. If T is covariant (for the class), then it is not any more safe to consume an object that can produce Ts than to consume Ts directly.
Example of how this could create a failure:
class Test11<T : Number> {
var list: MutableList<out T>? = null
fun test(list: MutableList<out T>) {
this.list = list
}
}
fun main() {
val test: Test11<Long> = Test11()
val test2: Test11<out Number> = test
val doubleList: MutableList<out Number> = mutableListOf(1.0)
test2.test(doubleList) // Not allowed
// if it were allowed:
val long: Long? = test.list?.firstOrNull() // ClassCastException casting the Double to a Long
}