【问题标题】:Kotlin: How can I invoke a lambda field which has a generic type of its class?Kotlin:如何调用具有其类的泛型类型的 lambda 字段?
【发布时间】:2016-05-10 06:40:01
【问题描述】:

如何调用具有其类的泛型类型的 lambda 字段?出于某种原因,对像Example<*> 这样的泛型类的引用会产生一个接受,它将原始类型(如Example<Something>)替换为Nothing。如何仅通过引用 Example<*> 来调用这样的 lambda?

我遇到这个问题的代码库:https://github.com/Jire/Acelta/tree/master/src/main/kotlin/com/acelta/packet/incoming/packets

我正在尝试此代码,但正如您在第 5 行看到的那样,我无法调用 lambda,因为接受类型是 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! */)
}

【问题讨论】:

  • 请在问题本身中添加您在编译/运行时遇到问题的特定代码。
  • @miensol 用法已添加。
  • 请尝试创建一个最小的、独立的示例。 PacketPacketeer 是如何定义的?
  • @KirillRakhman 您希望receive 的第二个参数是什么?

标签: generics lambda kotlin


【解决方案1】:

这种行为正是 star projections (Foo&lt;*&gt;) 在 Kotlin 中的工作方式。

如文档中所述

对于Foo&lt;T&gt;,其中T是一个不变量类型参数 绑定TUpperFoo&lt;*&gt;相当于Foo&lt;out TUpper&gt;进行阅读 值和Foo&lt;in Nothing&gt; 用于写入值。

如果你使用星形投影Packet&lt;*&gt;,你对它的泛型类型一无所知,因此你不能安全地将任何值传递给它的方法需要泛型参数(这就是Nothing类型存在的原因)。

您可以使用in-projection 为泛型类型指定下限,对于Packet&lt;in SomeType&gt;,您将能够将SomeType 的实例传递给方法。这将要求Packet 的实际类型参数为SomeType 或其某些祖先,类似于Java 中的? super T 通配符:

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

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

否则,您可以使用未经检查的强制转换来调用该方法,尽管它可能会导致 ClassCastException 在内部某处抛出:

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

【讨论】:

  • 有什么不安全的方法可以绕过这个限制吗?
  • @Jire,您可以执行不安全强制转换:(packet as Packet&lt;in SomeType&gt;).receive(packeteer, someTypeInstance),尽管它可能暗示 ClassCastException 被扔到某个地方。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多