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
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()
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.
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()
Thought I’d mention it in case someone found it interesting:
One kind of function that always returns the type of the class. Constructors
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.
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).
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?
}
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
If you need to access to private functions/fields, you have to implement function in companion object as:
open class Animal {
companion object {
// Defined extensions method for all types in companion object
fun <ExtendedClass: Animal> ExtendedClass.getThis(): ExtendedClass {
// this method can access to private methods/function of Animal object
return this
}
}
}
class Dog : Animal()
val dog: Dog = Dog().getThis()