Metalurgia
Metalurgia

Reputation: 497

Is there a way to declare a more specific generic type when overriding properties of a superclass in Kotlin?

I currently have a inheritance hierarchy as follows:

abstract class Foo {
    protected open var elem: Bar<out Foo>? = null
}

class Baz<T : Foo> : Foo {
    override var elem: Bar<out T>? = null
}

However, I'm getting the following error when I try to use this code

Var-property is Bar<out T>?, which is not a type of overriden protected open var elem: Bar<out Foo>? defined in Foo

I've also tried types such as:

abstract class Foo {
    protected open var elem: Bar<*>? = null
}

class Baz<T : Foo> : Foo {
    override var elem: Bar<out T>? = null
}

As well as:

abstract class Foo {
    protected open var elem: Bar<Foo>? = null
}

class Baz<T : Foo> : Foo {
    override var elem: Bar<T>? = null
}

However, in each of these cases, I still get similar error messages. I know in Java you can override an inherited variable with a more specific generic type. I'm not very familiar with Kotlin and only recently learned about declaration-site variance, so could someone explain what's going on here?

Upvotes: 1

Views: 514

Answers (1)

Horațiu Udrea
Horațiu Udrea

Reputation: 1877

Defining a more specific type for a mutable property would break inheritance and polymorphism rules. For example assume this would compile:

class A

class B :  A

abstract class Foo {
    protected open var elem: Bar<out A>? = null
}

class Baz : Foo {
    override var elem: Bar<out B>? = null
}

Then we have

val foo: Foo = Baz()
val producerOfA: Bar<out A> = foo.elem 

This is correct, the variable expects a producer of A and gets one (the specified type is Bar<out A> and the actual type is Bar<out B>, which is a subtype)

With the same declarations:

foo.elem = Bar<out A>()

This is incorrect, the setter expects a producer of A and gets one, but the underlying type for object foo is Baz() which accepts only producers of B (Bar<out B>), and of course a producer of A is not always a producer of B.

Hope this clears out the issue. You could make it compile by declaring immutable properties (put val instead of var), but this is use case specific.

Upvotes: 1

Related Questions