Аномалия дженериков Kotlin - PullRequest
0 голосов
/ 30 января 2019

Хорошо, у меня есть три типа интерфейса.

  1. Movement<T : Animal>
  2. Animal с подынтерфейсами Cat, Dog, Horse
  3. AnimalMovement

Интерфейс движения

interface  Movement<T : Animal> {
    fun moveAnimal(type:T)
}

Интерфейсы животных

interface Animal {
    fun takeSteps()
    fun jump()
    fun hide()
}

interface Cat : Animal
interface Dog : Animal

AnimalMovement

interface CatMovement : Movement<Cat>

Затем я реализую интерфейс CatMovement

class CatMovementImpl : CatMovement {
    override fun moveAnimal(type: Cat) {
        TODO("not implemented")        
    }
}

Проблема

fun TestGenerics() {
    var catMovement : Movement<Cat> = CatMovementImpl() // this works
    var catMovement : Movement<Animal> = CatMovementImpl() // this doesn't?
}

Я уверен в Javaобе линии работали бы нормально.Однако в Котлине вторая строка не выполняется.С чего бы это?Животное - это базовый тип кошки, так что это должно сработать, верно?

Ответы [ 2 ]

0 голосов
/ 31 января 2019

То, что moskito сказал в комментариях, правильно.

Я почти уверен, что это не работает и в Java.Movement<Cat> НЕ является подтипом Movement<Animal>, точно так же, как список НЕ является подтипом List<Object>.Возможно, вы захотите прочитать это.

Но в Kotlin это возможно с использованием дисперсии типов.

fun TestGenerics() {
    var catMovement1: Movement<Cat> = CatMovementImpl()
    var catMovement2: Movement<out Animal> = CatMovementImpl() // works
}

Вы в основном говорите компилятору "принять все реализации Movement<Animal> или реализацииMovement<S>, для которого S имеет Animal в качестве верхней границы ".

Но тогда возникает проблема.Вы не можете вызвать

val cat: Cat = /* ... */
catMovement2.moveAnimal(cat) // error

с сообщением об ошибке

Запроектированный тип Movement<out Animal> запрещает использование [...].

потому что T может использоваться только как производитель (вне позиции), а не как потребитель (в позиции), как это (функция, созданная для демонстрации сути):

val c: Cat = catMovement2.getAnimal() // works

Эта проблема становится понятной сразукогда вы используете out в объявлении Movement следующим образом:

interface Movement<out T : Animal> {
    fun moveAnimal(type: T) // error
}

Это зависит от вашего варианта использования, но, возможно, вам следует просто позволить Kotlin вывести тип, который будет CatMovementImpl.

var catMovement = CatMovementImpl()

Кредит переходит к EpicPandaForce за то, что он уже предложил использовать out в комментариях.

0 голосов
/ 30 января 2019

Я не эксперт Kotlin, но это кажется совершенно нормальным:

Когда объявлено так:

var animalMovement : Movement<Animal>

Вы можете написать код:

animalMovement.moveAnimal(dog)

Но если назначить это так:

var animalMovement : Movement<Animal> = CatMovementImpl()

разрешено, это означает, что ваш CatMovementImpl должен иметь возможность перемещать собаку?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...