In this article I am trying to explain generics in Kotlin, how it works and why it has in and out and how these are different than option available in Java.
First of first , lets understand how generic works in Java and what all we can and can’t do. So first we define a class in Java using generics. Lets say we want to implement our own Stack class which can accept any type of object.
public class Stack<T>{
public void push(T data) {}
public T pop() {}
}
We can also limit our stack to only work with certain type of Data. For ex if we only want to use numeric value we can write T extends Number.(Number is a parent class of all numeric object type in Java.)
There is nothing special about above code. Java’s collection framework works on same principle.
Now why i am explaining this??
We know Object is super type of every class in Java. So if we create Stack of String and try to assign it to Stack of Object it should work right?? Cause we are just assigning child type to super type. Lets try this
Stack<String> stringStack = new Stack<>();
Stack<Object> objectStack = stringStack; // Compile time error
But if you do above you will get compile time error. Why??
Simple, lets say Java does allow it and after this we do below
objectStack.push(45);
String str = stringStack.pop(); // Class Cast Exception
Therefore to avoid this, Java becoming as friendly as it is, it does not allow us and save us from having unexpected result in our code. I think you now get the point why this is not allowed in Java. Java is not a popular language, for no reason, we have lot of benefits.
Now lets say we want to be allowed to do this , cause we are smart programmer and trust we won’t do this, we can use wildcard in Java as below
Stack<? extends Object> objectStack = stringStack;
Now this works fine. The wildcard specify that this object accept item of type Object or any sub class and reading from it will return value of type Object.
But wait , now, we are not allowed to write to it.
objectStack.push(12); //Compile time error
This is because we do not know what object comply to what unknown subType of Object.
The technical term for this in covariant.
What it means ?? In simple terms We can successfully read from it but can not write to it due to reasons explained above. In this the said objectStack acts as producer only.
The opposite to it is contravariance where we can declare object which will only consume the value and will not produce anything.
This is also called read only from Producer(covariant) or write only to consumer(contravariance).
Now Kotlin goes one step further and has out and in keyword to use while declaring class which can acts only as producer or consumer.
class Stack<out Number>{} //Produces ie covariantOR
class Stack<in Number>{} // Consumer contravariance
The basic difference between Java and Kotlin is, Kotlin does it at the declaration site ie where class is declared and Java does it at use-site ie where object of class is created/used.
The in and out is not a new concept, this has been in use in programming for quite some time. So its nice to see Kotlin adapting these things.
For more info and further reading you can check these links.