Головная боль наследования котиновских дженериков

Я новичок в Kotlin, и я пытаюсь скомпилировать этот код без успеха (это всего лишь пример того, что я хочу сделать в реальном проекте):

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() } 

Я хочу переопределить свойство builder из класса Shape в классе Circle. Класс Shape знает только первый родовой тип из класса Builder. Второй может быть любым типом, поэтому я использую * для этого.

Когда я пытаюсь переопределить свойство builder в классе Circle, компилятор жалуется на сообщение:

  "Var-property type is 'Builder<String, Int>', which is not a type of overriden public abstract var builder: Builder<String, *>". 

Я также стараюсь с чем-то вроде «out Any» вместо *, без успеха.

Я не знаю, как скомпилировать этот код.

Когда вы переопределяете свойство var , компилятор

  • Получение значения переопределенного свойства является безопасным по типу: если исходное свойство имеет тип S , переопределенное свойство должно возвращать экземпляры S , то есть его тип должен быть S или его подтипом (например, безопасно возвращать Int когда Number необходим);

  • Установка значения переопределенного свойства безопасна по типу: если исходное свойство имеет тип S , переопределенное свойство должно принимать объекты S качестве значения, поэтому его тип должен быть S или некоторыми его супертипами (например, если нам нужно хранить String , сохраняя его как Any , будет ОК)

Учитывая эти два требования, единственным типом, который может иметь переопределенное свойство var , является исходный тип свойства, поскольку его подтипы и супертипы будут нарушать одно из вышеуказанных условий. Например, переопределенное свойство все равно должно сохранять Builder<T, *> и указывать его тип Builder<T, Int> не вариант:

 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 

Таким образом, вы просто не можете изменить тип свойства var когда вы его переопределяете, потому что с точки зрения генериков Kotlin он находится как in позиции, так и out .

Однако вы можете попробовать один из следующих вариантов:

  • Перейдите в свойство val . В этом случае нет второго требования, и вы можете изменить тип свойства на подтип исходного типа свойства:

     abstract class Shape<T: Any>(){ abstract val builder: Builder<T, *> } class Circle() : Shape<String>(){ override var builder: Builder<String, Int> = BuilderImpl() // OK } 
  • Добавьте общий параметр и укажите его в подклассе. В этом случае вы сможете использовать var , потому что Circle будет подтипом специализации Shape<String, Int> , а не оригинальным типом 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 }