Reputation: 650
I'm new to Kotlin and I'm trying to compile this code without success (this is only a example of I want to do in a real project):
abstract class Builder<T: Any, Y: Any>
class BuilderImpl() : Builder<String, Int>()
abstract class Shape<T: Any>(){
abstract var builder: Builder<T, *>
}
class Circle() : Shape<String>(){
override var builder: Builder<String, Int> = BuilderImpl()
}
I want to override builder property from Shape class in Circle class. Shape class only knows the first generic type from Builder class. The second one could be any type, so I use * for that.
When I try to override builder property in Circle class, compiler complains with the message:
"Var-property type is 'Builder<String, Int>', which is not a type of overriden public abstract var builder: Builder<String, *>".
I have also try with something like "out Any" instead of *, without success.
I'm don't have any idea about how to get this code compiled.
Upvotes: 4
Views: 688
Reputation: 147901
When you override a var
property, the compiler enforces that
getting the value of the overridden property is type-safe: if the original property has type S
, the overridden property should return instances of S
, that is, its type should be S
or its subtype (e.g. it's safe to return an Int
when a Number
is required);
setting the value of the overridden property is type-safe: if the original property has type S
, the overridden property should accept objects of S
as its value, so its type should be S
or some of its supertypes (e.g. if we need to store a String
, storing it as Any
would be OK)
Given these two requirements, the only type that an overridden var
property can have is the original property type itself, because its subtypes and supertypes will violate one of the above conditions. For example, the overridden property should still be able to store a Builder<T, *>
, and specifying its type to Builder<T, Int>
is not an option:
val s: Shape<SomeType> = Circle<SomeType>()
val b: Builder<SomeType, *> = someBuilder
s.builder = b // should be type-safe, but won't be with the override you want to do
So you simply cannot change the type of a var
property when you override it, because, in terms of Kotlin generics, it's in both in
and out
positions.
You can, however, try one of the following options:
Change to a val
property. In this case there's no second requirement, and you can change the type of the property to a subtype of the original property type:
abstract class Shape<T: Any>(){
abstract val builder: Builder<T, *>
}
class Circle() : Shape<String>(){
override var builder: Builder<String, Int> = BuilderImpl() // OK
}
Add a generic parameter and specify it in the subclass. In this case, you will be able to use var
, because Circle
will be a subtype of a specialization Shape<String, Int>
, not the original type Shape<T, R>
:
abstract class Shape<T: Any, R: Any>(){
abstract var builder: Builder<T, R>
}
class Circle() : Shape<String, Int>(){
override var builder: Builder<String, Int> = BuilderImpl() // OK
}
Upvotes: 3