Kotlin generics

01 Jun, 2020
Generics are the powerful features that allow us to define classes, methods and properties which are accessible using different data types while keeping a check of the compile-time type safety.
Creating parameterized classes –A generic type is a class or method that is parameterized over types. We always use angle brackets () to specify the type parameter in the program.
Generic class is defined as follows:
class MyClass<T>(text: T) {
    var name = text
}

To create an instance of such a class, we need to provide the type arguments:
val my : MyClass<String> = Myclass<String>("GeeksforGeeks")
If the parameters can be inferred from the arguments of constructor , one is allowed to omit the type arguments:
val my = MyClass("GeeksforGeeks") 

Here, GeeksforGeeks has type String, so the compiler figures out that we are talking about Myclass<String>
Advantages of generic –
Type casting is evitable- No need to typecast the object.Type safety- Generic allows only single type of object at a time.Compile time safety- Generics code is checked at compile time for the parameterized type so that it avoids run time error.
Type casting is evitable- No need to typecast the object.
Type safety- Generic allows only single type of object at a time.
Compile time safety- Generics code is checked at compile time for the parameterized type so that it avoids run time error.
In below example, we create an Company class with primary constructor having single parameter. Now, we try to pass the different type of data in object of Company class as String and Integer. The primary constructor of Company class accept string type (“GeeskforGeeks”) but gives compile time error when passes Integer type (12).
Kotlin program without generic class-
class Company (text: String) {    var x = text    init{        println(x)    }}fun main(args: Array<String>){    var name: Company = Company("GeeksforGeeks")    var rank: Company = Company(12)// compile time error}
Output:
Error:(10, 33) Kotlin: The integer literal does not conform to the expected type String

In order to solve above problem, we can create a generic type class which is user defined that accepts different type of parameters in single class. The class Company of type is a general type class that accepts both Int and String types of parameters.
Kotlin program using generic class-
class Company<T> (text : T){    var x = text    init{        println(x)    }}fun main(args: Array<String>){    var name: Company<String> = Company<String>("GeeksforGeeks")    var rank: Company<Int> = Company<Int>(12)} 
Output:
GeeksforGeeks
1234

Unlike Java, Kotlin makes arrays invariant by default. By extension, generic types are invariant in Kotlin. This can be managed by the out and in keywords. Invariance is the property by which a standard generic function/class already defined for a particular data type, cannot accept or return another datatype. Any is the supertype of all other datatypes.Variance is of two types-
Declaration-site variance(using in and out)Use-site variance: Type projection
Declaration-site variance(using in and out)
Use-site variance: Type projection
The out Keyword –
In Kotlin, we can use the out keyword on the generic type which means we can assign this reference to any of its supertypes. The out value can only produced by the given class but can not consumed:
class OutClass<out T>(val value: T) {
    fun get(): T {
        return value
    }
}

Above, we have defined an OutClass class that can produce a value of type T. Then, we can assign an instance of the OutClass to the reference that is a supertype of it:
val out = OutClass("string")
val ref: OutClass<Any> = out    

Note: If we have not used the out type in the above class, then given statement will produce a compiler error.
The in Keyword –
If we want to assign it to the reference of its subtype then we can use the in keyword on the generic type. The in keyword can be used only on the parameter type that is consumed, not produced:
class InClass<in T> {
    fun toString(value: T): String {
        return value.toString()
    }
}

Here, we have declared a toString() method that only be consuming a value of type T. Then, we can assign a reference of type Number to the reference of its subtype – Int:
val inClassObject: InClass<Number> = InClass()
val ref<Int> = inClassObject

Note: If we have not used the in type in the above class, then given statement will produce a compiler error.
Covariance implies substituting subtypes is acceptable, but supertypes is not, i.e. the generic function/class may accept subtypes of the datatype it is already defined for, e.g. a generic class defined for Number can accept Int, but a generic class defined for Int cannot accept Number. This can be implemented in Kotlin using the out keyword as follows-
fun main(args: Array<String>) {    val x: MyClass<Any> = MyClass<Int>()        // Error: Type mismatch    val y: MyClass<out Any> = MyClass<String>() // Works since String is a subtype of Any    val z: MyClass<out String> = MyClass<Any>() // Error since Any is a supertype of String}class MyClass<T>
We can directly allow covariance by appending out keyword to the declaration site. The following code works just fine.
fun main(args: Array<String>) {        val y: MyClass<Any> = MyClass<String>() // Compiles without error}class MyClass<out T>
It is used to substitute a supertype value in the subtypes, i.e. the generic function/class may accept supertypes of the datatype it is already defined for, e.g. a generic class defined for Number cannot accept Int, but a generic class defined for Int can accept Number. It is implemented in Kotlin using the in keyword as follows-
fun main(args: Array<String>) {        var a: Container<Dog> = Container<Animal>()  //compiles without error        var b: Container<Animal> = Container<Dog>()  //gives compilation error}open class Animalclass Dog : Animal()class Container<in T>
If we want to copy all the elements of an array of some type into the array of Any type then it can be possible, but to allow the compiler to compile our code we need to annotate the input parameter with the out keyword. This makes the compiler to infer that input argument can be of any type that is a subtype of the Any:
Kotlin program of copying elements of one array into another –
fun copy(from: Array<out Any>, to: Array<Any>) {    assert(from.size == to.size)    // copying (from) array to (to) array    for (i in from.indices)        to[i] = from[i]    // printing elements of array in which copied    for (i in to.indices) {    println(to[i])    }}fun main(args :Array<String>) {    val ints: Array<Int> = arrayOf(1, 2, 3)    val any :Array<Any> = Array<Any>(3) { "" }    copy(ints, any)  }
Output:
1
2
3

When we do not know about the specific type of the value and we just want to print all the elements of an array then we use star(*) projection.
Kotlin program of using star projections –
// star projection in arrayfun printArray(array: Array<*>) {    array.forEach { print(it) }}fun main(args :Array<String>) {    val name  = arrayOf("Geeks","for","Geeks")    printArray(name)}
Output:
GeeksforGeeks
kevilpy
Picked
Kotlin
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.