Kotlin Covariant Return types

It’s legal to substitute a child class as the return type of a base class when overriding a function. Doing so allows us to avoid unnecessary casting. Let’s look at an example.

abstract class Cook(val name : String) {

    //Abstract method that returns Cook
    abstract fun copy() : Cook
}

class Bob(name : String) : Cook(name) {

    /**
     * Notice how this method changes the
     * return type from Cook to Bob. This is legal
     * because Bob is a child class of Cook.
     *
     * This is called covariant return types
     */
    override fun copy(): Bob {
        return Bob(this.name)
    }
}

In this example, we have a base class Cook that has an abstract copy() method. The copy() method returns a Cook object. However, Bob overrides copy() and returns Bob. The difference may look small, but when used we do not need to downcast Cook objects back to Bob.

fun main(args : Array<String>){
    val bob = Bob("Bob")

    //No casting required since copy() returns a Bob
    val bobCopy : Bob = bob.copy()
    val cookCopy : Cook = bob.copy()

    println(bob.name)
    println(bobCopy.name)
    println(cookCopy.name)
}

If we had the copy() method in Bob return Cook rather than Bob, we would need to down cast.

class Bob(name : String) : Cook(name) {

    //This verion returns the Base Type
    override fun copy(): Cook {
        return Bob(this.name)
    }
}

val bobCopy : Bob = bob.copy() as Bob //Now a cast is required

So although we could have Bob.copy() return Cook, we now have to immediately downcast the result back into a Bob object. This doesn’t really make a lot of sense for a copy() method, which is why it’s acceptable to override a method by returning a child class type when needed.

Putting it Together

Here is an example program that shows off covariant return types.

package ch1.overriding

abstract class Cook(val name : String) {

    //Abstract method that returns Cook
    abstract fun copy() : Cook
}

class Bob(name : String) : Cook(name) {

    /**
     * Notice how this method changes the
     * return type from Cook to Bob. This is legal
     * because Bob is a child class of Cook.
     *
     * This is called covariant return types
     */
    override fun copy(): Bob {
        return Bob(this.name)
    }
}

fun main(args : Array<String>){
    val bob = Bob("Bob")

    //No casting required since copy() returns a Bob
    val bobCopy : Bob = bob.copy()
    val cookCopy : Cook = bob.copy()

    println(bob.name)
    println(bobCopy.name)
    println(cookCopy.name)
}

Here is the output when run

Bob
Bob
Bob

One thought on “Kotlin Covariant Return types”

Leave a comment