S-K'
S-K'

Reputation: 3209

Why can't Kotlin smart cast between an interface and the generic type that is derived from it?

I have the following class:

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

I receive a pre-compilation error stating:

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

To fix this pre-compilation error I am casting the ViewIntent object to I:

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

But why can't Kotlin detect that I must be derived from ViewIntent and smart cast it?

Upvotes: 2

Views: 1344

Answers (3)

Jacob Zimmerman
Jacob Zimmerman

Reputation: 1573

Basically, the reason why what you're doing doesn't work is because whatever I is is a subclass of ViewIntent. Your object is also a subclass ViewIntent. It's a completely different subclass. The cast you're doing is like trying to cast StringBuilder into a String.

Now let's discuss what I think you "want" to do and why that doesn't work either. In order to really get the result you want, you need to create the I type directly, like this:

return object : I {}

And in we replaced that I with an actual class,

return object : SomeClass {}

this would certainly fail, too. SomeClass's constructor needs to be called, and you're not doing it. And there's no way to know what to pass into that constructor when using the generic type.

Upvotes: 1

Egor Neliuba
Egor Neliuba

Reputation: 15054

That's because ViewIntent isn't I. See example:

class MyViewIntent : ViewIntent

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

Upvotes: 8

Raymond Arteaga
Raymond Arteaga

Reputation: 4673

It's just because "I" is NOT necessarily derived from ViewIntent, but exactly ViewIntent class.

You can fix it like this:

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

Doing it your way is really unsafe.

To understand why, I guess you should start reading this:

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

Upvotes: 5

Related Questions