Почему 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
нужно вызвать конструктор, а вы этого не делаете. И нет никакого способа узнать, что передать в этот конструктор при использовании универсального типа.