【问题标题】:Translating generic wildcards from Java to Scala将通用通配符从 Java 转换为 Scala
【发布时间】:2013-05-02 01:46:58
【问题描述】:

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

类型不匹配;找到:java.util.ArrayList[Number] required: java.util.List[Object] 注意:Number <: object java trait list e>

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

Collections.sort[Object](list, comparator)

至于具有可比列表的版本,我提出了以下声明:

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

但这根本不起作用:

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


我做错了什么? Scala 变体泛型是否遵循与 Java 不同的规则?如何调用Collections.sort 方法而不实际出现编译错误?

旁注:

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

【问题讨论】:

    标签: java scala generics


    【解决方案1】:

    您为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]) { ??? }
    

    【讨论】:

    • 啊,对了,我现在看到了错误!即使在 Java 中,客户端代码也需要是 Collections.&lt;Number&gt;sort(list, comparator),而不是 Collections.&lt;Object&gt;sort(list, comparator)
    • 另外,关于拆分参数列表的一个很好的说明,我没有意识到这一点!您能否详细说明带有Comparable列表的版本?
    • 将通配符拆分为两个类型参数的有趣方式,但似乎是正确的。谢谢。
    • @Natix 我认为您不能像以前那样使用通配符,因为类型搜索(_)可能会出现差异。在有两个参数的版本中,我们移除了从T(原_)到U的依赖。
    • Comparable版本,def sort[T &lt;: Comparable[T], U &lt;: T](list: List[U]) { ??? }def sort[T &lt;: Comparable[_]](list: List[U]) { ??? }有什么区别?当您查看字节码时,前者会产生`public , U> void sort(List a)`(所以不包括super),所以这几乎是和public &lt;T extends Comparable&lt;?&gt;&gt; void sort2(List&lt;T&gt; a)一样吗?
    【解决方案2】:

    您可以使用以下代码调用 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,所以这不是一个选项。

    【讨论】:

    • 啊,对了,我现在看到了错误!即使在 Java 中,客户端代码也需要是 Collections.&lt;Number&gt;sort(list, comparator),而不是 Collections.&lt;Object&gt;sort(list, comparator)
    • 你能详细说明一下带有Comparable列表的版本吗?顺便说一句,我认为Scala的列表类型参数实际上是covariantList[+A])。
    • @Natix:我希望我能回答您关于 Comparable 案例的问题。我最近刚从 Java 介绍到 Scala,而您所拥有的与我在 Java 中所做的直接等价(实际上这就是真正的实现所做的)。需要第二个类型参数似乎完全是矫枉过正。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    • 2013-05-11
    • 1970-01-01
    • 1970-01-01
    • 2014-09-04
    相关资源
    最近更新 更多