Intereting Posts
Одинаковая конечная точка отдыха с различными PathVariable Что такое простой MapRowParser в Anko? Неподвижная последовательность, функциональная / потоковая идиома Kotlin Как развернуть приложение Kotlin в Heroku? Kotlin DSL для создания объектов json (без создания мусора) Может ли Kotlin или Swift разобрать JSON так же, как Javascript? Как использовать выражение Lamba, чтобы сделать вызовы метода Java менее подробными в Kotlin? Исключение: blockingConnect не следует вызывать в потоке пользовательского интерфейса, несмотря на то, что я добавил другой поток Не удалось выполнить синхронизацию проекта Gradle, Котлин Ошибка внедрения Kotlin Как liveata отправляет данные в действие, если какие-либо изменения Как конвертировать файл Java в файл Kotlin? Как программно добавить вид в макет Constraint? Автоматическое обновление версии библиотеки для проектов Gradle в настоящее время не поддерживается. Обновите файл build.gradle вручную. Как настроить NDK с помощью Kotlin в Android Studio 3.0

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

Я новичок в 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 когда Требуется номер);

  • установка значения переопределенного свойства является безопасным по типу: если исходное свойство имеет тип 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 когда вы его переопределяете, потому что в терминах генериков Котлина он находится как 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 }