理解Java中的Collections.reverseOrder()方法

Edg*_*jān 9 java generics

考虑类中sort方法的一个重载定义Array:

public static <T> void sort(T[] a, Comparator<? super T> c)
Run Code Online (Sandbox Code Playgroud)

以相反顺序对数组进行排序的常用方法是将Comparator返回的for Collections.reverseOrder()作为第二个参数传递给此方法.

让我们看一下Collections.reverseOrder()openjdk 7 中方法的实现:

public static <T> Comparator<T> reverseOrder() {
    return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}
Run Code Online (Sandbox Code Playgroud)

ReverseComparator 类:

private static class ReverseComparator
    implements Comparator<Comparable<Object>>, Serializable {

    private static final long serialVersionUID = 7207038068494060240L;

    static final ReverseComparator REVERSE_ORDER = new ReverseComparator();

    public int compare(Comparable<Object> c1, Comparable<Object> c2) {
        return c2.compareTo(c1);
    }

    private Object readResolve() { return reverseOrder(); }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:为什么Collections.reverseOrder()要成为通用的?为什么根本ReverseComparator.REVERSE_ORDER无法归还?

当然,我们可以指定显式调用类型Collections.<T>reverseOrder().但Collections.reverseOrder()在这种情况下,对简单的好处是什么?

我发现那里有一个非常有用的讨论:

Collections.reverseOrder()如何知道要使用的类型参数?

但它没有回答我的问题.

同时这对我来说多么有趣sort的方法使用compare方法从ReverseComparator类.我们可以看到compare接受Comparable<Object>类型的参数.如果我们对实现的对象数组进行排序,例如Comparable<T>在哪里呢?我们不能调用与原因不强制转换为.TIntegercompareComparable<Integer>Comparable<Integer>Comparable<Object>

Mik*_*kis 7

为什么Collections.reverseOrder()是通用的?

此函数是通用的,以便您不必将结果转换为特定Comparable<T>类型.(你可能会说你不在乎,因为你还是不管它,在这种情况下,这告诉我们你没有启用足够的警告.)

为什么我们不能简单地返回ReverseComparator.REVERSE_ORDER?

一个原因是因为ReverseComparator.REVERSE_ORDER包是私有的,所以你不能从包外面访问它.这反过来又引出了一个问题:"为什么包装私有?" 好吧,主要是因为这满足了纯粹主义者,当他们看到成员变量被直接访问时,即使他们是最终的,他们也会畏缩,但实际上我不会责怪他们在这种情况下,因为访问者在二进制级别提供向前兼容性,这可能是完全没有必要的在应用程序代码中,它在语言运行时变得必不可少.并且ReverseComparator是Java运行时的一部分.

但更重要的原因是因为Collections.reverseOrder()演员会(Comparator<T>)为你做,所以你不必自己动手.(同样,如果你没有看到这个问题,那是因为你没有启用足够的警告,这意味着你需要重新考虑你的做法.)

简而言之,如果您尝试执行以下操作:

Comparator<MyObject> myComparator = ReverseComparator.REVERSE_ORDER;
Run Code Online (Sandbox Code Playgroud)

你会得到一个错误,因为这是一个无效的任务.所以,您必须将其更改为:

Comparator<MyObject> myComparator = 
    (Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;
Run Code Online (Sandbox Code Playgroud)

但是你会得到一个警告,因为这是一个未经检查的演员.所以你最终必须这样做:

@SuppressWarnings( "unchecked" )
Comparator<MyObject> myComparator = 
    (Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;
Run Code Online (Sandbox Code Playgroud)

这是丑陋的.所以,Collections.reverseOrder()从中拯救你,允许你这样做:

Comparator<MyObject> myComparator = Collections.reverseOrder();
Run Code Online (Sandbox Code Playgroud)

我们可以看到compare接受了Comparable类型的参数.如果我们对实现Comparable的对象数组进行排序,其中T例如是Integer,该怎么办?我们无法调用与可比较原因的比较Comparable不会转换为Comparable.

好的,我看到你真正的问题是什么.欢迎来到java泛型和类型擦除的精彩世界.我将尝试解释,但一定要查找"类型擦除"一词,以便完全理解这个概念.

在java中,泛型作为事后的想法被引入到语言中.出于这个原因,它们必须以这样的方式实现,即泛型感知代码将向后兼容旧代码,而不是泛型感知.解决方案是一种称为类型擦除的技巧,它基本上意味着在编译后完全剥离通用信息.这意味着,在字节码级别,Comparator<String>Comparator<Integer>Comparator是同一个东西.没有任何区别.这使得java运行库能够实现一个类,该类充当任何对象的反向比较器.这是不是真的一个Comparator<Object>,这是一个Comparator<ANYTHING>,因为它是所有反转比较的方向,因此它并不真正关心被比较的对象的性质.

因此,在java中,如果您真的知道自己在做什么,则可以自由地将泛型类的实例强制转换为同一个类的实例,但使用不同的泛型参数.在这种情况下,java运行时的创建者正在转换Comparator<Object>Comparator<T>,实际上可能稍后将其分配给Comparator<Integer>,这很好.

这个演员虽然很棘手,因为编译器必须相信你真的知道你在做什么,所以默认情况下,编译器会在这些演员表上发出"未经检查的作业"警告,然后我们表示我们发誓我们知道什么我们正在做一个@SuppressWarnings( "unchecked" )注释.

Collections.reverseOrder() 让你不必担心这一切.