Почему Kotlin не может выполнять умный приведение между интерфейсом и производным от него универсальным типом?

У меня есть следующий класс:

abstract class PresenterActivity<S : ViewState, I : ViewIntent> : AppCompatActivity() { 
    open fun initViewIntent(): I {
        return object : ViewIntent{} // type mismatch on this line
    }
}

Я получаю сообщение об ошибке предварительной компиляции:

Type mismatch
Required: I
Found: <S, I>

Чтобы исправить эту ошибку перед компиляцией, я приводю объект ViewIntent к I:

abstract class PresenterActivity<S : ViewState, I : ViewIntent> : AppCompatActivity() { 
    open fun initViewIntent(): I {
        @Suppress("UNCHECKED_CAST")
        return object : ViewIntent{} as I
    }
}

Но почему Котлин не может обнаружить это I должен быть получен из ViewIntent а умный бросил это?

2 ответа

Решение

Это просто потому, что "Я" НЕ обязательно является производным от ViewIntent, но именно класса ViewIntent.

Вы можете исправить это так:

abstract class PresenterActivity<S : ViewState, I : ViewIntent> : AppCompatActivity() { 
    open fun initViewIntent(): ViewIntent {
        return object : ViewIntent{} 
    }
}

Делать это по-своему небезопасно.

Чтобы понять почему, я думаю, вы должны начать читать это:

https://blog.kotlin-academy.com/kotlin-generics-variance-modifiers-36b82c7caa39

https://kotlinlang.org/docs/reference/generics.html

https://proandroiddev.com/understanding-generics-and-variance-in-kotlin-714c14564c47

Это потому что ViewIntent не I, Смотрите пример:

class MyViewIntent : ViewIntent

class MyPresenterActivity : PresenterActivity<..., MyViewIntent>() {
    // inherited from PresenterActivity
    open fun initViewIntent(): ViewIntent {
        return object : ViewIntent{} as MyViewIntent // you see where this breaks
    }
}

По сути, причина того, что вы делаете, не работает, потому что все I это подкласс ViewIntent, Ваш объект также является подклассом ViewIntent, Это совершенно другой подкласс. В ролях, которые вы делаете, все равно что пытаться StringBuilder в String,

Теперь давайте обсудим, что, я думаю, вы "хотите" сделать и почему это тоже не работает. Чтобы действительно получить желаемый результат, вам нужно создать I введите напрямую, вот так:

return object : I {}

И мы заменили это I с реальным классом,

return object : SomeClass {}

это, конечно, тоже не получится. SomeClassнужно вызвать конструктор, а вы этого не делаете. И нет никакого способа узнать, что передать в этот конструктор при использовании универсального типа.

Другие вопросы по тегам