Jire
Jire

Reputation: 10280

Kotlin: How can I invoke a lambda field which has a generic type of its class?

How can I invoke a lambda field which has a generic type of its class? For some reason, a reference to such a generic class like Example<*> yields an accept that replaces the original type (like Example<Something>) with Nothing. How can I invoke such a lambda by only having reference to Example<*>?

Codebase I am having this problem with: https://github.com/Jire/Acelta/tree/master/src/main/kotlin/com/acelta/packet/incoming/packets

I am trying to this code, but as you can see on the 5th line I cannot invoke the lambda because the accept type is Nothing.

private val incoming = arrayOfNulls<Packet<*>>(256)

fun incoming(id: Int, packeteer: Packeteer) {
    val packet = incoming[id] ?: return
    packet.receive(packeteer, /* this is type Nothing! */)
}

Upvotes: 1

Views: 1187

Answers (1)

hotkey
hotkey

Reputation: 147901

This behavior is just how star projections (Foo<*>) work in Kotlin.

As said in the docs

For Foo<T>, where T is an invariant type parameter with the upper bound TUpper, Foo<*> is equivalent to Foo<out TUpper> for reading values and to Foo<in Nothing> for writing values.

If you use star projection Packet<*>, you know nothing about its generic type, thus you cannot safely pass any value to its method where a generic parameter is expected (that's why Nothing type is there).

You can use in-projection to specify a lower bound for the generic type, and for Packet<in SomeType> you will be able to pass instances of SomeType into the methods. This will require actual type parameter of Packet to be SomeType or some of its ancestors, similar to ? super T wildcard in Java:

private val incoming = arrayOfNulls<Packet<in SomeType>>(256)

fun incoming(id: Int, packeteer: Packeteer) {
    val packet = incoming[id] ?: return
    packet.receive(packeteer, someTypeInstance)
}

Otherwise, you can use unchecked cast to call the method, though it can result in ClassCastException thrown somewhere inside:

(packet as Packet<in SomeType>).receive(packeteer, someTypeInstance)

Upvotes: 2

Related Questions