class MyLinkedList<TYPE> {
class Node<TYPE>(
val value: TYPE,
var next: Node<TYPE>? = null
)
private var head: Node<TYPE>? = null
var size: Int = 0
private set
fun insert(value: TYPE) {
head = Node(value, head)
size++
}
}
fun <ITEM_TYPE> copy4(from: MyLinkedList<in ITEM_TYPE>, to: MyLinkedList<outITEM_TYPE>) {
(0 until from.size).forEach {
to.insert(from[it])//under from[it] it says:
/**Type mismatch.
Required:
Nothing
Found:
Any?**/
}
}
I am swapping āinā and āoutā intentionally, to understand the error.
I know the issue resides on āinā and āoutā, but just want to know.
I understand it like this : it is related to the fact that āoutā is just for returning and we are using it a position of an argument that is from[it] āitā here an item from āfromā argument that is defined as out, but it is in a position of argument."
1- is my understanding corrct?
2- And why compiler interpreted them into āNothingā and āAny?ā?
3- does the compiler catch the type of āfromā and baed on that, it treat the logic of āinā and āoutā?
My instructor explains: āwhen we float over this, itās saying that āIām requiring nothingā thatās because the ātoā side that says āIām taking something as an output parameterā, āI canāt call anything that actually takes an argument coming inā so it actually replaces the type of those arguments with nothing.ā
in/out basically means that we donāt know what types are produced/consumed respectively. So this is similar to a star projection, If we produce and we donāt know what is the type, we still can safely assume it is at least Any?, because all objects are Any?. If we consume, but we donāt know what type to use, we canāt call the function safely. In this case Kotlin uses Nothing to represent an āimpossibleā type, so we canāt pass anything there.
out (covariant) means roughly āā¦or a subtypeā.
in (contravariant) means roughly āā¦or a supertypeā.
(Those relationships are clearer, though more long-winded, in Java, where covariance is indicated by ? extends <type>, and contravariance by ? super <type>.)
In this case, inside copy4() all we know is that to is a MyLinkedList of some subtype of ITEM_TYPE. But thatās all we know ā we canāt narrow it down to any particular subtype. At the most extreme, it could be Nothing (which is a subtype of all types).
Similarly, from is a MyLinkedList of some supertype of ITEM_TYPE ā but again, the compiler canāt say anything about which supertype, except that itās Any? or some subtype.
So the only type we know we can insert() into to is Nothing ā but what weāre trying to insert could be Any?. Hence the error message.
Thank you very much.
3d question is when the compiler catches the from type. Let say the type here is Animal. Based on this it enforces the second argument to be its subtype that is Cat or Dog?
I think this is probably one of the very few things about Kotlin that I actually donāt like and I think Java does better. I have NEVER managed to wrap my head around in and out and where youāre supposed to use one and not the other. I understand the basic concept of in is for consuming and out is for producing, but when I try to actually use it in code, I always get it wrong. I think Javaās syntax is easier to understand.
for me in/out or super/extends are a different view of the problem,
one describes the usage in=consumer out=producer (which are reasonable names, right?), the other is a language theoretical thing (which explains which types you can use).
Maybe the problem with something like copy(from, to), is that you think of the parameters as from=read=in / to=write=out. But thatās a different thing.
Looking at the type it seems to be clear, from uses a type that produces values (out) and to uses a type that consumes values (in).
So, from is something you read, but you are reading from the other end which is a producer.
I personally like in / out because itās a simpler model (not type theory), but I also have to remember, that itās the other end of what I usually think first.
On the other side, with super / extends I have to think about the consequences the data direction has for the types, which is not directly obvious to me.
Fortunately, I never had to learn modern Java.
One way of thinking about this is that out will make all <T> arguments to methods into Nothing. This is because you said the type can only be output, not taken in ; that means nothing is known about the type that can be taken in (there can be arbitrary limits on it) so you can only disallow any values to be passed, because any value may not match the constraints. So the compiler uses the type that admits no value, which is called Nothing.
Conversely, in will make all <T> return values into Any?, because you marked the type as being only inputtable. You do know the output methods output something, but since you know nothing about the value you use the type that can be anything, which is called Any?.