Запечатанный класс в Котлине, ошибка несовместимых типов

У меня есть следующий код Kotlin. Запечатанный класс Animal и два класса объектов Dog и Cat наследуются от закрытого класса Animal. Я получаю эту ошибку в предложении when в случае Cat.

Incompatible types: Cat and Dog

Почему выдает эту ошибку? Как я могу использовать запечатанный класс в Котлине для операций этого типа? Закрытый класс - хороший выбор для полиморфизма?

sealed class Animal {
  abstract fun speak()
}

object Dog : Animal() {
    override fun speak() { println("woof") }
}

object Cat : Animal() {
    override fun speak() { println("meow") }
}

fun main(args: Array<String>) {
    var i = Dog
    i.speak()
    when(i) {
        is Dog -> {
            print("Dog: ")
            i.speak()
        }
        is Cat -> {
            print("Cat: ")
            i.speak()
        }
    }
}

person s-hunter    schedule 06.01.2018    source источник


Ответы (2)


Недостающая часть var i: Animal = Dog

В основном компилятор жалуется на типы - Cat не является подтипом Dog (но они оба являются подтипами Animal, поэтому, если вы явно установите код базового типа, код будет компилироваться и работать

person ruX    schedule 06.01.2018
comment
Есть ли в этом случае какие-либо преимущества использования запечатанного класса по сравнению с абстрактным классом? Подходит ли запечатанный класс для такого полиморфизма? - person s-hunter; 06.01.2018
comment
@ s-hunter, Запечатанные классы подходят, когда у вас есть закрытый набор типов и вам нужна открытая функциональность. Добавляемая вами функциональность обычно будет реализована по-разному для каждого типа в наборе, не требуя, чтобы типы соответствовали какому-то общему интерфейсу. - person chris; 06.01.2018
comment
@ s-hunter ну, запечатанные классы на самом деле не связаны с полиморфизмом, они просто дают семантику для описания конечной иерархии и используют ее, например, в конструкции when - person ruX; 06.01.2018
comment
@ruX, это все-таки полиморфизм. Если у вас есть животное и функция, которая работает с любым животным, вы вызываете функцию, и ее поведение зависит от фактического типа животного, которое у вас есть. Вы можете изменить фактический тип, не меняя вызова функции, и получить другое поведение. Основное различие между этим и основанным на наследовании полиморфизмом состоит в том, что для написания новых функций не требуется ничего изменять, а для добавления новых типов - требуется. - person chris; 06.01.2018

В вашем коде есть два места, которые компилятор в целом не понимает:

  1. Внутри предложения when вы проверяете, действительно ли ваша переменная типа Dog is Dog.
  2. Внутри предложения when вы также проверяете, является ли ваша переменная типа Dog Cat.

Это немного противоречит компилятору, поскольку оба типа имеют общий супертип друг с другом. На самом деле проблема в том, что ваша переменная не явно объявляет свой тип. В результате присвоения экземпляра Dog вашему var i компилятор определяет его тип, которым, конечно же, является Dog. После этого все имеет смысл: нет необходимости проверять тип экземпляра, это определенно Dog.

Чтобы код заработал, вы должны объявить var i: Animal, явно типизированный. Кроме того, всегда рассматривайте возможность использования val вместо var.

person s1m0nw1    schedule 06.01.2018