Reputation: 10280
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
Reputation: 147901
This behavior is just how star projections (Foo<*>
) work in Kotlin.
As said in the docs
For
Foo<T>
, whereT
is an invariant type parameter with the upper boundTUpper
,Foo<*>
is equivalent toFoo<out TUpper>
for reading values and toFoo<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