First lets clearfy what do we get by prefixing a type parameter with out keyword. consider the following class:
class MyList<out T: Number>{
private val list: MutableList<T> = mutableListOf()
operator fun get(index: Int) : T = list[index]
}
out keyword here makes MyList covariant in T, which essentially means you can do the following :
// note that type on left side of = is different than the one on right
val numberList: MyList<Number> = MyList<Int>()
if you remove the out keyword and try to compile again, you will get type mismatch error.
by prefixing the type parameter with out, you are basically declaring the type to be a producer of T's, in the example above MyList is a producer of Numbers.
which means no matter if you instantiate T as Int or Double or some other subtype of Number, you will always be able to get a Number from MyList (Because every subtype of Number is a Number). which also allows you to to do the following:
fun process(list: MyList<Number>) { // do something with every number }
fun main(){
val ints = MyList<Int>()
val doubles = MyList<Double>()
process(ints) // Int is a Number, go ahead and process them as Numbers
process(doubles) // Double is also a Number, no prob here
}
// if you remove out, you can only pass MyList<Number> to process
Now lets answer With out keyword why T should only be in return position? and what can happen without this constraint?, that is if MyList had a function taking T as parameter.
fun add(value: T) { list.add(T) } // MyList has this function
fun main() {
val numbers = getMyList() // numbers can be MyList<Int>, MyList<Double> or something else
numbers.add(someInt) // cant store Int, what if its MyList<Double> ( Int != Double)
numbers.add(someDouble) // cant do this, what if its MyList<Int>
}
// We dont know what type of MyList we going to get
fun getMyList(): MyList<Number>(){
return if(feelingGood) { MyList<Int> () }
else if(feelingOk> { MyList<Double> () }
else { MyList<SomeOtherSubType>() }
}
that is why constraint is required, its basically there to guarantee type safety.
as for abstract class E<out T> (t:T) { val x = t } being compiled, Kotlin In Action has following to say
Note that constructor parameters are in neither the in nor the out
position. Even if a type parameter is declared as out, you can still
use it in a constructor parameter. The variance protects the class
instance from misuse if you’re working with it as an instance of a
more generic type: you just can’t call the potentially dangerous
methods. The constructor isn’t a method that can be called later
(after an instance creation), and therefore it can’t be potentially
dangerous.