Generics
Kotlin provides higher order of variable typing called generics. As in java classes in kotlin may have type parameters .The idea is to allow type (Integer, String,.....etc and user defined types ) to be a parameter . like it would be nice to write a method which could work on the elements in an integer array a string array or an array of any type
class Id(t:T){
var value = t
}
//to create an instance we need to provide the type argument
val id : Id<Int> = Id<Int> (1)
But if the parameters may be infered eg from the constructor argument or by some other means one is allowed to omit the type arguments
val id = Id(1)
//1 has type int so the compiler
//figures out that we are talking about Id<Int>
//1 has type int so the compiler
//figures out that we are talking about Id<Int>
In other languages like java has a type system is wildcard types and in kotlin doesn't have any instead it has two other things called as declaration-sits and type projections
Declaration-site variance
suppose we have generic interface Source<T> that does not have any methods that takes T as a parameter ,only methods that return T . in kotlin there are two key words in and out if we can annotate the type parameter T of Source to make sure it only returned(produced)from members of Source<T> and never consumed means that the produced means you can only read from statement
and cannot write
Declaration-site variance
suppose we have generic interface Source<T> that does not have any methods that takes T as a parameter ,only methods that return T . in kotlin there are two key words in and out if we can annotate the type parameter T of Source to make sure it only returned(produced)from members of Source<T> and never consumed means that the produced means you can only read from statement
and cannot write
interface Source<out T>{
fun nextT():T
}
fun demo (strs:Source<String>){
val objects:Source<Any> = strs
//this is okay since T is an out parameter
}
fun nextT():T
}
fun demo (strs:Source<String>){
val objects:Source<Any> = strs
//this is okay since T is an out parameter
}
The general rule is when a type parameter T of a class C is declaraed out it may occur only in out-position in the members of C but in return C<Base> can safely be a supertype of C<Derived>
in clever words they say that the class C is covariant in the parameter T or that T is a covariant type parameter think C as being producer of T's and NOT a consumer of T's
The out modifier is called a variance annotation and since it is provided at the type parameter declaration site we talk about declaration site variance
The other key word is in it makes a type parameter contravariant it can only be consumed and never produced as example :
in clever words they say that the class C is covariant in the parameter T or that T is a covariant type parameter think C as being producer of T's and NOT a consumer of T's
The out modifier is called a variance annotation and since it is provided at the type parameter declaration site we talk about declaration site variance
The other key word is in it makes a type parameter contravariant it can only be consumed and never produced as example :
interface Comparable <in t>{
operator fun compareTo(other: T):Int
}
fun demo (x:Comparable<Number>){
x.compareTo(1.0)
//1.0 has type Double which is a subtype of Number
//thus we can assign x to a variable of type comparable<Double>
val y:Comparable<Double> = x//OK!
}
operator fun compareTo(other: T):Int
}
fun demo (x:Comparable<Number>){
x.compareTo(1.0)
//1.0 has type Double which is a subtype of Number
//thus we can assign x to a variable of type comparable<Double>
val y:Comparable<Double> = x//OK!
}
Type projection
Use-site variance:Type projections
It some cases some classes can't actually be restricted to only return T's A good example of this Array
class Array (val size:Int){
fun get (index : Int):T{/*....*/}
fun set(index: Int,value:T){/*....*/}
}
This class cannot be either co-or contravariant in T And this imposes certain inflexibilities here is an example
fun copy(from:Array<Any>,to:Array<Any> ){
assert (from.size == to.size)
for (i in from.indices)
to[i] = from [i]
}
assert (from.size == to.size)
for (i in from.indices)
to[i] = from [i]
}
This function is supposed to copy items from one array to another Lets try to apply it in practice:
val ints: Array<Int> = arrayOf(1,2,3)
val any = Array<Any>(3){ "" }
copy (ints,any)
//Error : expects (Array <Any>,Array<Any>)
val any = Array<Any>(3){ "" }
copy (ints,any)
//Error : expects (Array <Any>,Array<Any>)
Here Array <T> is invariant in T thus neither of Array <Int> and Array<Any> is a subtype of the other because copy might be doing bad things it might attempt to write say a String to from and if we actually passed an array of Int there a ClassCastException would have been thrown sometime later
so we make sure that copy doesn't do any bad things like we need to prevent it from writing and we can :
so we make sure that copy doesn't do any bad things like we need to prevent it from writing and we can :
fun copy (from:Array<out Any>,to:Array<Any>){
//......
}
//......
}
What has happened here is called type projection the from is not simply an array but restricted (projected) one: we can only call those methods that return the type parameter T In this case it means that we can only call get() .this is our approach to use site variance
you can also project a type with in as well
you can also project a type with in as well




