How do I return `this` from a method of a subclass?

I want to return this from a method for easy configuration after class creation, something like apply(). I also want to be able to subclass. So I wrote this:

open class Animal {
    fun getThis() = this
}

class Dog : Animal()

val dog: Dog = Dog().getThis()

Which doesn’t work due to an error on the last line:

Type mismatch: inferred type is Animal but Dog was expected

I also tried this:

open class Animal {
    fun <T: Animal> getThis(): T = this
}

Which doesn’t compile due to error on line 2:

Type mismatch: inferred type is Animal but T was expected

A similar extension method works, however:

open class Animal

fun <T : Animal> T.getThis(): T = this

Can I do this without using extension methods?

Hasty reply. Working copy in answer below.
open class Animal<T: Animal> {
    fun getThis(): T =  this
}
class Dog: Animal<Dog>
val d: Dog =  Dog().getThis()
2 Likes

This doesn’t compile:

One type argument expected for class Animal
Type mismatch: inferred type is Animal but T was expected
This type has a constructor, and thus must be initialized here

sry, was typing this withou actually trying it out. It has been a while since I l last used something like this. This should work:

abstract class Animal<T: Animal<T>>{
    abstract fun getThis(): T
}
class Dog: Animal<Dog>() {
    override fun getThis() = this
}
val d: Dog =  Dog().getThis()
1 Like

I don’t think you need generics if you are overriding methods. This works:

abstract class Animal{
    abstract fun getThis(): Animal
}

class Dog: Animal() {
    override fun getThis() = this
}

val d: Dog =  Dog().getThis()

But I am creating a lot of classes and I hoped that I can avoid duplicating the method everywhere…

The pattern @Wasabi375 demonstrates, parameterising a class with itself, is pretty common…⠀It seems like a bit of a hack to me, but it’s used in Class, KClass, the parent class of enums, and many other places too.

It’s discussed in this StackOverflow question.

If I understand correctly, it’s basically a work-around for not having self types in the language.

The C++ equivalent apparently rejoices in the name Curiously-Recurring Template Pattern (CRTP).

4 Likes

Aha, thanks for the links! I suppose this is what I want, then.

open class Animal<This: Animal<This>> {
    @Suppress("UNCHECKED_CAST")
    fun getThis() = this as This
}

class Dog: Animal<Dog>()

val dog: Dog = Dog().getThis()
2 Likes

Thought I’d mention it in case someone found it interesting:
One kind of function that always returns the type of the class. Constructors :wink:

Dart has named constructors so you could in Dart make an Dog.getThis() function. OP’s function is a member function so it’s not the same since constructors are members of the type not an instance. And of course adding named constructors to Kotlin wouldn’t solve the problem.

EDIT: Haha yes it’s definitely cheating :laughing:

Constructors are cheating!⠀ Bad Dog()!!⠀ :smile:

(At least partly because they’re not inherited, like other methods, so don’t suffer this particular problem.)

1 Like

speaking of constructors, what is the reason that they are not inherited?

A constructor needs to set all properties of a class so you nearly always have to write a custom constructor anyways. Also there are cases where you don’t want all the constructors of a superclass and instead want a different set of constructor parameters.
It is very easy to add a constructor that just calls super but there is no syntax in kotlin for removing a super constructor for the subclass, because this is also not possible for normal methods.
Another reason is that constructors are conceptually methods of the type and not the class. You can think of them as a special kind of method that belongs to the companion object. Those methods are also not inherited by subclasses (although you can still call them).

1 Like

An exercise that might help to understand is to try to figure out what it would be like for this example’s Dog class to inherit from the Animal constructor:

open class Animal {
    val name: String
    val age: Int
    
    // Using Java-style constructor just to emphasize it.
    constructor(name: String, age: Int) {
        this.name = name
        this.age = age
    }
}

class Dog : Animal {
    val breed: String
    val barkVolume: Int
}

fun main() {
    // Imagine this class `Dog` somehow inherited Animal's constructor.
    val myDog = Dog("Rex", 3) // What would this return?
}

Aha. Thanks for the answer @Wasabi375!

method that belongs to the companion object. Those methods are also not inherited

That’s a shame, I miss being able to write… (not real code hehe)

abstract class Animal private constructor(
    val name: String,
    val age: Int
) {
    abstract fun makeNoise(): String
    
    companion object {
        fun fromString(string: String): ThisClass {
            val (name, ageStr) = string.split(" ")
            val age = ageStr.toInt()
            return ThisClass(name, age)
        }
    }
}

class Dog : Animal {
    override fun makeNoise() = "Woof —said $name the dog"
}

val dogNoise = Dog.fromString("Bobby 17").makeNoise()

@arocnies You are not initializing breed and barkVolume though