Generics In Kotlin (Part 3)

NEHAL KUMAR
CodeX
Published in
4 min readMay 14, 2022

Welcome to Part 3 of the series on Generics.

If you are learning Generics for the first time please check

Part 1 with Java examples :

Part 2 with Kotlin Basics:

In this post I want to go over more concepts around Generics in Kotlin while comparing with Java as needed. As mentioned earlier, the best way to learn these would be to try out the examples in your preferred IDE :)

Star Projection

It might feel weird to have a separate bog post dedicated to Star Projection but let’s make sure we understand all the concepts behind it.

Kotlin’s star projection is similar to unbounded wildcard in Java which we use when we know nothing about the type but still want to use in a safe way.

While this is the simple definition, let’s understand why Star Projection exists and how do we make use of this concept. There are multiple reasons/scenarios to use Star Projection:

  1. We DO NOT know the type to specify(this is an easy one!)
  2. We are NOT INTERESTED in using any functions having T in its signature for a Generic class declared with parameter T
  3. We DO NOT want to use T specific functions accidentally!

Let’s understand through some code samples. I would encourage to try this sample code in your IDE and immediately see the difference:

class Foo<in T> {

fun accept(value: T){
println(value)
}
}
fun <T> usingFoo(con: Foo<T>, t: T){
con.accept(t) //Allowed
}
fun usingFooStar(con: Foo<*>, t: Any){
con.accept(t) // Not Allowed
}

As you can see in the sample code above, we are only able to use the methods having T in its signature when we have a specific type. Let’s first understand why this makes sense.

When we use Star Projection, the compiler has no idea of the Type. So anything that depends on the type for verification is not safe anymore to be used. It will be a trouble again if something works during Compile Time and fails at Runtime.

Let’s see what is allowed with specific type vs star projection when you use these methods and that clarifies why certain things are not allowed.

Declared Foo with a Type and used usingFoo method:

//Case 1(Valid): Foo is declared with Number type & 7 is Int which is Subtype of Number.val fooNum : Foo<Number> = Foo()
usingFoo(fooNum, 7)
//Case 2(Invalid & Compile time failure): Foo is declared with type Int and we are trying to store a Double which is NOT Subtype of Intval fooNum : Foo<Int> = Foo()
usingFoo(fooNum, 7.0)

Let’s now use usingFooStar method

//Case 3(No compile time failure here but you are not allowed to use accept method from Foo class as mentioned above!)val fooString : Foo<String> = Foo()
usingFooStar(fooString, 7)
//Case 4(No compile time failure here but you are not allowed to use accept method from Foo class as mentioned above!)val fooAny : Foo<*> = Foo<Any>()
usingFooStar(fooAny, 7)

I’ll let you now understand why the compiler blocks you from using the methods that have T in its signature! If we did allow calling the Type related methods, they will then fail at Runtime since we cannot verify the types.

Comparison with Wildcards

Kotlin blogs are incomplete without Java references you see :)

Kotlin’s star projection is similar to unbounded wildcards which we use in a similar way:

Probably one thing to notice is that we are allowed to pass null for Foo<T> or Foo<?> in Java code but they are not allowed in Kotlin unless we explicitly mark them optional. I think that is what the Kotlin official blog also conveys with this line:

Star-projections are very much like Java’s raw types, but safe.

Hopefully this short post clarifies details on Star Projections. Adding some helpful resources that helped me understand these topics better.

Resources

Part 1 & 2 of the series:

--

--