Scala 2.10 中的具体泛型

Posted

技术标签:

【中文标题】Scala 2.10 中的具体泛型【英文标题】:Reified generics in Scala 2.10 【发布时间】:2011-12-22 14:33:15 【问题描述】:

Scala 中缺乏具体化的泛型是该语言最困扰我的地方,因为如果不使用复杂的构造,simple things 就无法实现。

Kotlin 和 Ceylon 都支持物化泛型,因此绝对可以在 JVM 之上这样做。在past 中,据说Scala 无法在不更改JVM 的情况下支持它们,但现在Scala 2.10 是rumored,对具体化的支持有限。所以我的问题是:

我们可以期待 Scala 2.10 中的具体化,例如,我是否能够实现 generic trait multiple times ?到底有多大限制? 如果 Scala 2.10 的具体化结果比Kotlin 和Ceylon 更有限。这是为什么呢?

【问题讨论】:

我尝试更新问题,以便现在希望更容易回答(如是/否,您不能在 Scala 2.10 中多次实现通用特征,因为类型具体化需要在生成的类中进行名称修改,让它们在 Java 中使用起来很复杂。或者不管答案是什么。 Odersky 物化是在 2.10,所以这不仅仅是谣言。 您的意思是不存在的 Kotlin 和 Ceylon-that-was-just-release-without-reification 表明物化泛型是可能的?它是如何工作的? 我无法理解这个问题怎么会被认为是“没有建设性的”。这个问题非常客观,并且有明确的答案,尽管提及的语言/功能尚不可用。 【参考方案1】:

你的论点有缺陷。 Kotlin 还没有发布*,Ceylon 刚刚发布了它的第一个版本,我将引用 their announcement 中缺少的一个东西:

具体化的泛型

所以,对不起,但是 什么 实现证明这是可能的?事实上,我并没有过多关注 Kotlin 的前景,但 Ceylon 所承诺的正是 manifest 已经提供的东西,而且是以透明的方式提供的。

但是让我们考虑一下您在问题中描述的问题:

trait Handles[E <: Event] 
  def handle(event: E)

所以,首先,JVM 没有提供任何方法来识别接口或类中的类型参数,所以E 无法被 JVM 检查。但是,您可以在实现 Handles 的每个对象中存储有关 E 代表什么的信息,就像您可以在 Scala 中编写此代码一样:

abstract class Handles[E <: Event : Manifest] 
  def handle(event: E)

接下来,我们来看handle的方法。同样,JVM 没有提供在方法定义中使用类型参数的方法。实现它的唯一方法是让handle 接受Object 作为参数:即,键入擦除。

这就是交易:要使handle 可以从Java 中调用,它必须被类型擦除。而且,如果它被类型擦除,那么它会受到您问题中描述的限制。解决这个问题的唯一方法是放弃 Java 兼容性(顺便说一下,这在 Ceylon 的第一个版本中也不可用)。

是的,根据 Martin Odersky 的说法,Scala 将在 2.10 上进行(某种形式的)具体化。但无论它提供什么(我打赌更透明地使用清单来断言类型相等),这个特殊的限制是 JVM 固有的,如果不放弃 Java 集成就无法克服。

(*) Kotlin 现在有一个演示,它的具体化——到目前为止——只是捆绑清单和 instanceOf 测试的语法糖。它仍然受到 Scala 的所有相同限制。

【讨论】:

我认为既然规范提到了具体化(fx.goo.gl/jMvzG),作者确实找到了一种方法来做到这一点。如果不是,那么你是完全正确的。我的问题更多是关于 2.10 中泛型的性质以及为什么 kotlin 和 ceylon 可以解决这个问题而 Scala 不能(假设他们可以)。 @Lars:不,如果不在 JVM、类文件、字节码和语言之间创建一个全新的抽象,就无法在 JVM 上进行适当的具体化泛型。尽管有两种新语言声称支持具体化的泛型,但这些语言迄今为止除了一些美化的 instanceOf 测试之外什么也没提供。 Gosu 支持物化泛型,尽管 Kotlin 和 Ceylon 没有发布支持它们的版本,但毫无疑问,它们也会支持它们。话虽如此,我认为在 Scala 中添加具体化的泛型是一个错误,会对该语言造成影响,而需要访问泛型信息的情况非常罕见,并且通常可以通过清单充分满足。戈苏链接:gosu-lang.org/doc/Gosu%20Reference%20Guide/wwhelp/wwhimpl/… 虽然我知道这个星球上没有任何事实可以改变你的观点,但你真的检查过你的主张吗? :-) 只是一个示例“错误:具有相同名称的方法在字节码级别具体化为相同的签名。重命名其中一个方法。”。请注意“擦除”如何方便地被“具体化”取代?如果编译器在尝试实现简单接口时不会完全不稳定并崩溃(“java.lang.ClassFormatError: Illegal field modifiers in class program_/__Program__689”),我可以继续。 顶部的一行更新说明,会和你刚刚留下的评论一样快。【参考方案2】:

Kotlin 已经为内联函数类型参数重新定义了泛型,如下所述:https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters。这在 Kotlin 中已经存在了一段时间,它们已被 Kotlin 生态系统中的许多库使用。当提到 Kotlin 时,这里的其他答案已经过时了。 Kotlin 自 2016 年 2 月起以 1.0 的形式发布。

Kotlin 中的具体化泛型示例,Jackson 中著名的TypeReference,在Jackson Kotlin module 中使用时使用此代码:

public inline fun <reified T: Any> ObjectMapper.readValue(jp: JsonParser): T 
     = readValue(jp, object: TypeReference<T>() )

来自基于 Kotlin 的 Injekt 库的相同内容:

public inline fun <reified T: Any> fullType(): FullTypeReference<T> 
    = object:FullTypeReference<T>()

public inline fun <reified T : Any> injectLazy(): Lazy<T> 
    return lazy  Injekt.get(fullType<T>()) 

【讨论】:

【参考方案3】:

根据 Andrey Breslav 在 this 页面上所说的话,Kotlin 没有具体类型:

“是的,类对象中没有类型参数”

【讨论】:

Kotlin 中删除了 Reified 类型:“Reified generics 只能在 JVM 上实现,性能开销很大,我们决定取消此功能。”。见devnet.jetbrains.com/message/5479326#5479326 同时,Ceylon 的下一个里程碑版本将包含 reified generics ceylon-lang.org/blog/2013/02/21/reification-finally @Ollip 您的答案应该更新,Kotlin 确实为内联函数重新定义了泛型,这些泛型被大量使用并解决了最常见的情况。您的回答现在对当前的 Kotlin 具有误导性。

以上是关于Scala 2.10 中的具体泛型的主要内容,如果未能解决你的问题,请参考以下文章

java泛型中的通配符

Scala的类与类型

Java泛型中的“超级”和“扩展”有啥区别[重复]

Kotlin 泛型中的 in 和 out

Java泛型中的通配符

java 泛型中的T和?