2

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?

S-K'
  • 3,188
  • 8
  • 37
  • 50
  • 1
    `I` being derived from `ViewIntent` isn't _enough_ to make that a valid cast. In fact, `ViewIntent` has to be derived from `I` to make the thing you're describing work. – Louis Wasserman Jun 05 '18 at 19:07

3 Answers3

8

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
    }
}
Egor Neliuba
  • 14,784
  • 7
  • 59
  • 77
5

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

Raymond Arteaga
  • 4,355
  • 19
  • 35
1

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.

Jacob Zimmerman
  • 1,521
  • 11
  • 20