【问题标题】:Scala generics: Int not conforming to Comparable?Scala泛型:Int不符合Comparable?
【发布时间】:2011-07-24 09:33:44
【问题描述】:

以下 Scala 声明是可以的:

trait Base[B <: Base[B,M,ID], M <: Meta[B,M,ID], ID <: Comparable[ID]] {
    // ...
}

trait Meta[B <: Base[B,M,ID], M <: Meta[B,M,ID], ID <: Comparable[ID]] extends Ordered[Meta[_,_,_]] {
    // ...
}

trait BaseWithID[B <: BaseWithID[B,M,ID], M <: Meta[B,M,ID], ID <: Comparable[ID]] extends Base[B,M,ID] with Ordered[B] {
    // ...
}


trait BaseWithIntID[B <: BaseWithIntID[B,M,ID], M <: MetaWithIntID[B,M,ID], ID <: Comparable[ID]] extends BaseWithID[B,M,ID] {
    // ...
}

trait MetaWithIntID[B <: BaseWithIntID[B,M,ID], M <: MetaWithIntID[B,M,ID], ID <: Comparable[ID]] extends Meta[B,M,ID] {
    // ...
}

但以下两个不是:

trait BaseWithIntID[B <: BaseWithIntID[B,M], M <: MetaWithIntID[B,M]] extends BaseWithID[B,M,Int] {
    // ...
}

trait MetaWithIntID[B <: BaseWithIntID[B,M], M <: MetaWithIntID[B,M]] extends Meta[B,M,Int] {
    // ...
}

不同之处在于我去掉了 BaseWithIntID 和 MetaWithIntID 中的 ID 类型参数,并在各自的基本特征中明确指定了 Int。但这不会编译,那么这是否意味着 Int 在 Scala 中不可比较?如果是,我做错了什么?我尝试了 Ordered 而不是 Comparable,但这并没有什么不同。

我正在使用 Eclipse,和往常一样,错误消息没有帮助:

type arguments [B,M,Int] do not conform to trait BaseWithID's type parameter bounds [B <: BaseWithID[B,M,ID],M <: Meta[B,M,ID],ID <: java.lang.Comparable[ID]]

它只是说有问题,但没有说哪个类型参数错了,为什么。看着this question,我想我可以尝试“ID

实际上,这也不起作用(带有相同的错误消息):

trait TestBase extends BaseWithID[TestBase,TestMeta,Int]

trait TestMeta extends Meta[TestBase,TestMeta,Int]

【问题讨论】:

  • ID &lt;% Comparable[ID] 的问题在于它自动定义了一个隐式参数。由于特征不允许有参数,所以它不起作用。但是,作为class,您可以做到。

标签: generics scala int comparable


【解决方案1】:

Int 在 scala 中确实没有可比性,当然因为它实际上是作为 java int 实现的,而不是 java.lang.Integer。我不确定这是不可能的,C#struct(值类型)可以实现接口,但这里没有这样做。

您通常会说在您的 ID 类型的隐式范围内有一个 Ordering 可用,ID : Ordering

举个简单的例子:

import Ordering.Implicits._
def max[A : Ordering](x: A, y: A) : A = if (x > y) then x else y

这相当于将 Ordering(与 java.util.Comparator 相同)传递给函数。确实,声明

def max[A : Ordering](x: A, y: A)

翻译成

def max[A](x: A, y: A)(implicit ev: Ordering[A])

其中ev 是一个新名称。如果 A : Ordering 出现在类上而不是方法定义上,就像在您的代码中一样,它会转换为构造函数的隐式参数,如果需要,该参数将保存在字段中,并且在类的隐式范围内可用。这比强制 A 为Comparable(scala 中的Ordered)更灵活,因为它可能用于不属于您且尚未实现 Comparable 的类。您也可以在同一类的不同 Odering 之间进行选择,只要反转默认设置即可:Ordering 上有一个 def reverse : Ordering 方法可以做到这一点。

不利的一面是,VM 不太可能内联对比较方法的调用,但也不太可能使用泛型中的接口方法。

在 java 中实现 Comparable&lt;T&gt; 的类型通过对象 Ordering 中的隐式方法 (ordered) 自动获得隐式范围内的 Ordering。 java Comparator&lt;T&gt; 也可以转换为Ordering (Ordering.comparatorToOrdering)。

Ordering[A] 在隐式范围内时,导入 Ordering.Implicits._ 允许您使用漂亮的 x &gt; y 语法。

【讨论】:

    【解决方案2】:

    “这是否意味着 Int 在 Scala 中不可比?”的答案?显然是的,因为如果我用 java.lang.Integer 替换 Int,那么它编译时不会出错。那么问题来了,我最终不得不在每次访问 ID 时创建一个包装对象,这会经常发生,因此成本很高。

    我想指定 ID 是 Comparable/Ordered,以便我可以使 BaseWithID 本身有序,并使用可比较的 ID 在其中显式定义 compare 方法。

    目前的解决方案似乎是不指定 ID 是有序的,让具体类实现自己比较,而不是在 trait 中实现一次。有人有更好的解决方案吗?

    【讨论】:

    • 为了补充我的答案,在“昂贵”方面,对比较方法的调用 - Ordering.comare(x,y) - 可能不会被内联,您也可能需要付费。那么它会在Java中使用Comparable。另一方面,可以调用 x > y 的包装意味着 new ord.Ops(x).>(y),从实现的角度来看是 new Ordering.Ops(ord, x),其中 ord 是排序。很可能可以避免创建此对象。如果需要(基准测试),您可以直接调用 ordering.compare,这不会比在 java 中调用 compare 差多少。
    • P.S. “可能避免创建此对象”是指“VM 可能实际上不会创建该对象”。通过不使用 x > y 语法,不仅可以,而且可以肯定您可以避免创建对象。
    猜你喜欢
    • 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
    相关资源
    最近更新 更多