Понимание различий между инициализацией и назначением в Kotlin Generics - PullRequest
1 голос
/ 31 января 2020

Я читал раздел о ключевых словах "in and out" в Kotlin из книги "Kotlin Программирование - Руководство ранчо для больших ботаников". Я пришел из C ++ фона, где инициализация и присваивание являются двумя очень разными понятиями. Следующий фрагмент кода из книги (слегка измененный) вводит меня в заблуждение.

Код выглядит следующим образом:

class Barrel<out T>(val item:T)
open class Loot(val value: Int)
class Fedora(val name:String, value:Int) : Loot(value)

public fun main(){
    var fedoraBarrel: Barrel<Fedora> = Barrel(Fedora("a generic-looking fedora", 15))
    var lootBarrel: Barrel<Loot> = fedoraBarrel
    lootBarrel = fedoraBarrel
    val myFedora: Fedora = lootBarrel.item
}

Здесь, когда я комментирую строку

lootBarrel = fedoraBarrel

В следующей строке я получаю следующую ошибку, когда пытаюсь получить элемент Fedora из бочки, и ошибка:

Error:(27, 28) Kotlin: Type mismatch: inferred type is Loot but Fedora was expected

Чем назначение fedoraBarrel для lootBarrel отличается от инициализации lootBarrel с помощью Федора Баррель. Зачем мне нужна строка

lootBarrel = fedoraBarrel 

для этого кода для компиляции?

Ответы [ 2 ]

3 голосов
/ 31 января 2020

Почему линия var lootBarrel: Barrel<Loot> = fedoraBarrel бессмысленна?

Это не бессмысленно, а наоборот; Вы явно запрашиваете более общий тип, компилятор разумно предполагает, что вы действительно хотите, чтобы lootBarrel имели тип Barrel<Loot>, поэтому lootBarrel.item имеет тип Loot, а не Fedora.

После

lootBarrel = fedoraBarrel 

компилятор видит, что ему присвоено значение типа Barrel<Fedora>, поэтому его можно локально обрабатывать как имеющего этот тип, пока он не изменится.

Может ли он работать без переназначения? Да. Это обсуждается в https://youtrack.jetbrains.com/issue/KT-13663, с текущим заключением

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

Предыдущий комментарий дает обходной путь, в вашем случае

var lootBarrel: Barrel<Loot>
lootBarrel = fedoraBarrel
val myFedora: Fedora = lootBarrel.item

(обратите внимание, это инициализация, а не присвоение)

2 голосов
/ 31 января 2020

Давайте упростим ваш случай до следующего кода:

var a: Number = 0
a + 1 // Raises an error, class Number does not have `plus` function
a = 0
a + 1 // Works fine

Когда вы инициализируете переменную (var a: Number = 0), компилятор устанавливает тип a в Number независимо от типа выражения, используемого в качестве начальное значение (оно не приводит к a к Int, поскольку вы указали тип явно).

Когда вы пишете a = 0, происходит умное приведение, поэтому вы можете работать с a, как с Int, пока вы снова не измените a.

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