简体   繁体   English

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

[英]Translating generic wildcards from Java to Scala

In the java.util.Collections class, we have two variants of the sort method, one that takes a list of arbitrary objects with a corresponding Comparator : java.util.Collections类中,我们有两种sort方法的变体,一种是使用相应的Comparator获取任意对象的列表:

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

And one that takes a list of Comparable objects: 并且获取可Comparable对象列表:

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

I was thinking how one woulds translate such method signatures with bounded wildcards into Scala. 我在想如何用有界通配符将这种方法签名翻译成Scala。 For the first version, I translated the signature literally and at first sight without compilation problems: 对于第一个版本,我从字面上翻译了签名,一眼就没有编译问题:

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

But then I found that I could not invoke this method with the following arguments: 但后来我发现我无法使用以下参数调用此方法:

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

The last line gives this compile error, even though I explicitly specify the type T as Object . 最后一行给出了这个编译错误,即使我明确地将类型T指定为Object

type mismatch; 类型不匹配; found : java.util.ArrayList[Number] required: java.util.List[Object] Note: Number <: Object, but Java-defined trait List is invariant in type E. You may wish to investigate a wildcard type such as _ <: Object . found:java.util.ArrayList [Number] required:java.util.List [Object]注意:Number <:Object,但Java定义的特征List在类型E中是不变的。您可能希望调查通配符类型,例如_ <: Object (SLS 3.2.10) (SLS 3.2.10)

Actually, I found out that it's even impossible to call the sole Java method directly as it fails with the same type of error. 实际上,我发现甚至不可能直接调用唯一的Java方法,因为它失败并且具有相同类型的错误。

Collections.sort[Object](list, comparator)

As for the version with comparable list, I came up with this declaration: 至于具有可比列表的版本,我想出了这个声明:

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

But this doesn't work at all: 但这根本不起作用:

illegal cyclic reference involving type T 涉及T类的非法循环引用


What am I doing wrong? 我究竟做错了什么? Are Scala variant generics following different rules the the Java ones? Scala变体泛型是否遵循Java规则的不同规则? How call one call the Collections.sort method without actually getting a compile error? 如何调用一个调用Collections.sort方法而不实际得到编译错误?

Side note: 边注:

No, I'm not really asking how I can sort a list in Scala. 不,我真的不知道如何在Scala中对列表进行排序。 I know that Scala has its own set of collections, sort functions and a different approach to comparing objects (such as Ordered and Ordering traits). 我知道Scala有自己的一组集合,排序函数和一种比较对象的不同方法(例如OrderedOrdering traits)。 My question concerns the general problem of generic methods and translation of generics from Java to Scala. 我的问题涉及泛型方法的一般问题以及从Java到Scala的泛型的翻译。

You are giving the wrong type parameter for T : You sort a List[Number] , not a List[Object] : 您为T提供了错误的类型参数:您对List[Number]排序,而不是List[Object]

sort[Number](list, comparator)

will work. 将工作。

If you want to call sort without a type argument, you need to define two argument lists (because of how type-inference works in Scala): 如果要在没有类型参数的情况下调用sort,则需要定义两个参数列表(因为类型推断在Scala中的工作方式):

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

// Then
sort(list)(comparator)

You might want to consider using Scala types, that have proper support for covariance (ie in Scala a List[Number] is a List[Object] ). 您可能需要考虑使用对协方差有适当支持的Scala类型(即在Scala中, List[Number]List[Object] )。

Concerning the version with comparable, you'll have to explicitly write the wildcard: 关于具有可比性的版本,您必须显式编写通配符:

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

You could call the Java variant (or yours) with: 您可以使用以下命令调用Java变体(或您的变体):

Collections.sort[Number](list, comparator)

The problem here is because Java generic types are invariant. 这里的问题是因为Java泛型类型是不变的。 In other words, this fails in Java: 换句话说,这在Java中失败了:

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

In Scala, generic type parameters can be declared to be covariant or contravariant in their declaration. 在Scala中,泛型类型参数可以在声明中声明为协变或逆变。 Scala's List type parameter is declared to be covariant (which works because it's immutable). Scala的List类型参数被声明为协变(这是有效的,因为它是不可变的)。 In other words, this is valid: 换句话说,这是有效的:

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

But since you're using Java's java.util.List that's not an option. 但是因为你使用Java的java.util.List不是一个选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM