Generics in Kotlin

Generics is one of the many topics found in almost all the languages which needs to be studied with a little bit of more focus and understanding of the basic concepts and rules associated with it.

In this blog post, I will create some sample code for a class and a function of Generic type for the understanding. Before diving into that let’s look into the fact as to why we use Generics in any programming language?

If you are aware of the basic’s of Kotlin language, you might have seen that the collection framework like listOf(), arrayListOf() returns you the list as per the type(Int, String) arguments passed. This is possible only because of Generics!

Without Generics, you would have created Multiple methods each having different types of the parameter to return that specific type (For Example Int or String or Float) of result and that would have introduced a lot of Boiler Code. This is where Generics come to our rescue. It follows the DRY(Do not repeat yourself) principle.

Let’s look how we can implement generics in Kotlin for a function:

fun <T> getListOf(word1: T, word2: T): List<T> {

    return arrayListOf(word1, word2)
}

fun main() {
    println(getListOf(1, 2))
    println(getListOf("hello", "Sarab"))
    println(getListOf(2.0, 3.0))
}

If you run above code, you will see the following output:

[1, 2]
[hello, Sarab]
[2.0, 3.0]

Let’s have a look, what we did and what does it mean? After the fun keyword, we have created <>(angular bracket) which signify that the corresponding method will be a Generic method. Inside the angular bracket, I have used the character T which signify the Type of the method( You can use your own character, this is customizable).

Moving on, we tell the compiler that this method will receive two words as an argument both of Type T and will return a List of Type T only. Inside the method body, we are just creating an ArrayList of both the words supplied and returning the result back to the calling function.

Let’s look into one more example to understand the other aspects of the generics in a function

fun <T : Number, S> concat(word1: T, word2: S): S {
    return word2.toString() + "" + word1
}
fun main() {
    println(concat(1, "Sara"))
    //println(concat("2", "Sara")) Give a compile time error
    println(concat(1, 2));

}

Output looks like this:

Sara1
 21

Let’s look into the code, so as you can see correctly, we have defined two Generic Types separated by comma(T and S). Moreover, we have also mentioned that the Type T will be a subtype of Number.

As you know Subtype of Number can be( Int, Float, Double, etc ). Moving on, our method will be returning a String type.

Inside the method, we are just concatenating the two Strings and returning the result. In our main function, if we try to pass a String as a first parameter we get a compile-time error because we have already declared in the method declaration that first parameter of Type T will a subtype of Number class.

Now Let’s have a look at how we can make a class Generic with a simple example:

class SampleGenerics<E>(val elements: ArrayList<E>) {

    fun removeLastElement(): List<E> {

        if (!elements.isEmpty()) {
            elements.removeAt(elements.size - 1)
        } else {
            println("List is Empty")
        }
        return elements;
    }

    fun printElements() {
        println(elements)
    }
}

fun main() {
    val nameList = arrayListOf("Sarabjit", "Singh", "Bagga")
    val generics = SampleGenerics(nameList)
    println(generics.printElements())
    println(generics.removeLastElement())

    val intList = arrayListOf(1, 2, 3)
    val generics2 = SampleGenerics(intList)
    println(generics2.printElements())
    println(generics2.removeLastElement())

}

The output looks like this:

[Sarabjit, Singh, Bagga]
[Sarabjit, Singh]
[1, 2, 3]
[1, 2]

As you can see above declaring a class Generic is somewhat similar to declaring a function Generic. Generic Name is defined right after the class name SampleGenerics<E> where E is the Generic Type. If we look at the class definition, we have declared that the Constructor of the class will accept an ArrayList of Type E.

Inside the class, we have created a method which removes the last element from the Generic List which is received as an Input and return the updated list.

This is how Generics are implemented in Kotlin at class and function level. In order to deep dive more into generics, one must have an understanding of the following terms: Variance, Contravariance, Covariance, and Invariance. We will cover these terms in our Next Blog Post for Generics in Kotlin and look at how it is different than the one used in Java. Till then Stay Tuned…

1 thought on “Generics in Kotlin”

Comments are closed.