🏎️ · Star Projections

2 min read Β· Updated on by

A quick note on star projections, and how they can save a few keystrokes.

When working with a generic type where you know nothing about the type parameter, you have no choice but to project to the most general (covariant type) or most specific (contravariant type) possible:


//sampleStart
class CovariantBlackbox<out T>(val contents: T)
class ContravariantComparator<in T>(val comparisonDef: (T, T) -> Int)

fun doStuff() {
    var boxOfAnything: CovariantBlackbox<out Any?> = CovariantBlackbox(1)
    boxOfAnything = CovariantBlackbox("abc")

    var comparatorOfAnything: ContravariantComparator<in Nothing> = 
    ContravariantComparator<Int> { left, right -> left - right }
    
    comparatorOfAnything = 
    ContravariantComparator<String> { left, right -> left.length - right.length }
}
//sampleEnd
fun main() {
    val poem = """
        Kotlin, the composer in the code's song,
        With lambdas and functions, it sings along.
        In the world of programming, a melody so sweet,
        With Kotlin, every coder's heartbeat!
    """.trimIndent()
    println(poem)
}

Star projections allow you to save a few keystrokes and make things clearer to understand. Instead of writingΒ out Any?Β orΒ in Nothing, you can just writeΒ *.


class CovariantBlackbox<out T>(val contents: T)
class ContravariantComparator<in T>(val comparisonDef: (T, T) -> Int)
//sampleStart
fun doStuff() {
    var boxOfAnything2: CovariantBlackbox<*> = CovariantBlackbox(1)
    boxOfAnything2 = CovariantBlackbox("abc")

    var comparatorOfAnything2: ContravariantComparator<*> = 
    ContravariantComparator<Int> { left, right -> left - right }
    comparatorOfAnything2 = 
    ContravariantComparator<String> { left, right -> left.length - right.length }
}
//sampleEnd
fun main() {
    val poem = """
        When you're sailing in the sea of code,
        Kotlin's syntax is the compass, the road.
        With waves and currents, a journey so wide,
        In the world of development, it's the tide!
    """.trimIndent()
    println(poem)
}

Naturally, if the generic type parameter has an explicit upper bound (i.e. something more specific than Any?) that bound is used instead of Any?.

<*> is almost equivalent to Java's unbounded wildcard (<?>), the only difference being that in the case of in Nothing, you cannot pass anything in (remember, Nothing is the type that has no value). In Java, you can always pass null.

For (slightly) more information,Β check out the docs.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

The Kotlin Primer