将通用通配符从Java转换为Scala

时间:2013-05-07 23:38:56

标签: java scala generics

java.util.Collections类中,我们有两个sort方法的变体,一个采用具有相应Comparator的任意对象列表:

public static <T> void sort(List<T> list, Comparator<? super T> comparator)

其中一个列出Comparable个对象:

public static <T extends Comparable<? super T>> void sort(List<T> list)

我在想如何将带有边界通配符的方法签名转换为Scala。对于第一个版本,我从字面上翻译了签名,一见钟情就没有编译问题:

def sort[T](list: List[T], comparator: Comparator[_ >: T]) { ??? }

但后来我发现我无法使用以下参数调用此方法:

val comparator = new Comparator[Object] {
    def compare(o1: Object, o2: Object) = ???
}
val list = new ArrayList[Number]
sort[Object](list, comparator)

最后一行给出了这个编译错误,即使我明确地将类型T指定为Object

  

类型不匹配; found:java.util.ArrayList [Number] required:java.util.List [Object]注意:Number&lt ;: Object,但Java定义的特征列表在类型E中是不变的。您可能希望调查通配符类型,例如_ <: Object。 (SLS 3.2.10)

实际上,我发现甚至不可能直接调用唯一的Java方法,因为它失败并出现相同类型的错误。

Collections.sort[Object](list, comparator)

对于具有可比列表的版本,我想出了这个声明:

def sort[T <: Comparable[_ >: T]](list: List[T]) { ??? }

但这根本不起作用:

  

涉及类型T的非法循环引用


我做错了什么? Scala变体泛型是否遵循Java规则的不同规则?如何在没有实际编译错误的情况下调用Collections.sort方法?

旁注:

不,我真的不知道如何在Scala中对列表进行排序。我知道Scala有自己的集合,排序函数和比较对象的不同方法(例如OrderedOrdering特征)。我的问题涉及泛型方法的一般问题以及从Java到Scala的泛型的翻译。

2 个答案:

答案 0 :(得分:4)

您为T提供了错误的类型参数:您对List[Number]进行了排序,而不是List[Object]

sort[Number](list, comparator)

会奏效。

如果要调用不带类型参数的sort,则需要定义两个参数列表(因为类型推断在Scala中的工作方式):

def sort[T](list: List[T])(comparator: Comparator[_ >: T]) { ??? }

// Then
sort(list)(comparator)

您可能需要考虑使用对协方差有适当支持的Scala类型(例如,在Scala中,List[Number]List[Object])。

关于具有可比性的版本,您必须明确编写通配符:

def sort[T <: Comparable[T], U <: T](list: List[U]) { ??? }

答案 1 :(得分:3)

您可以使用以下命令调用Java变体(或您的)

Collections.sort[Number](list, comparator)

这里的问题是因为Java泛型类型是不变的。换句话说,这在Java中失败了:

List<Number> l1;
List<Integer> l2 = l1; //contravariance fails
List<Object> l3 = l1; //covariance fails

在Scala中,泛型类型参数可以在声明中声明为协变或逆变。 Scala的List类型参数被声明为协变(这是有效的,因为它是不可变的)。换句话说,这是有效的:

val l1: List[Number] = ???
val l2: List[Object] = l1 //valid

但是因为你使用的Java java.util.List不是一个选项。