Is there anything wrong in this Generic Interface or Kotlin compiler is not able to infer types?

Viewed 93

Assume we have a Adapter interface:

interface EventAdapter<E : Event> {
    val event: E
    // ...
}

And a few implementations:

inline class MessageCreateAdapter(override val event: MessageCreateEvent) : EventAdapter<MessageCreateEvent> { /* ... */ }
inline class MessageUpdateAdapter(override val event: MessageUpdateEvent) : EventAdapter<MessageUpdateEvent> { /* ... */ }
// ...

And we create an extension to the Event to get respective Adapters:

fun <E : Event> E.toAdapter(): EventAdapter<E>? {
    return when (this) {
        is MessageCreateEvent -> MessageCreateAdapter(this)
        is MessageUpdateEvent -> MessageUpdateAdapter(this)
        else -> null
    }
}

Then the compiler complaints that the MessageCreateAdapter and MessageUpdateAdapter are not EventAdapter<E>?. I cannot figure out what's the problem, or the case where this does not hold.

enter image description here Attached a minimal compilation error message

2 Answers

Compiler infers type of that when expressinon as a most specific supertype of MessageCreateAdapter and MessageUpdateAdapter (+ nullable), which is EventAdapter<out Event>?. The problem is that compiler is not trying to proof that evaluation of this expression is dependent of E, so that resulting type could be not just most specific supertype of all when branches, but emphasized via E, so you need to help him and do some manual casting:

@Suppress("UNCHECKED_CAST")
fun <E : Event> E.toAdapter() : EventAdapter<E>? {
    return when(this) {
        is MessageCreateEvent -> MessageCreateAdapter(this) as EventAdapter<E>
        is MessageUpdateEvent -> MessageUpdateAdapter(this) as EventAdapter<E>
        else -> null
    }
}

For a case where this doesn't hold: when is MessageCreateEvent (for example) matches, it doesn't mean that E is MessageCreateEvent; it could be e.g. just Event. In that case you are required to return an EventAdapter<Event>. But you return a MessageCreateAdapter which extends EventAdapter<MessageCreateAdapter>, not EventAdapter<Event>.

If you used interface EventAdapter<out E : Event>, I think it could theoretically work, but still won't for the reason given in Михаил Нафталь's answer.

Related