理解Java

时间:2015-11-27 22:29:30

标签: java generics

考虑来自sort类的Array方法的重载定义之一:

public static <T> void sort(T[] a, Comparator<? super T> c)

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

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

public static <T> Comparator<T> reverseOrder() {
    return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}

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(); }
}

我的问题是:为什么Collections.reverseOrder()是通用的?为什么简单地ReverseComparator.REVERSE_ORDER无法返回?

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

我在那里找到了一个相当有用的讨论:

How does Collections.reverseOrder() know what type parameter to use?

但它没有回答我的问题。

同样有趣的是sort方法如何使用compare类中的ReverseComparator方法。我们可以看到compare接受Comparable<Object>类型的参数。如果我们对实现Comparable<T>的对象数组进行排序,其中T例如Integer怎么办?我们无法使用compare Comparable<Integer>调用Comparable<Integer>,因为Comparable<Object>未投放到v coord1 coord2 coord3 color1 color2 color3 f n1 n2 n3

2 个答案:

答案 0 :(得分:7)

  

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

此功能是通用的,因此您无需将结果转换为特定的Comparable<T>类型。 (你可能会说你不关心,因为你无论如何都没有投入,在这种情况下,这告诉我们你没有启用足够的警告。)

  

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

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

但更重要的原因是因为Collections.reverseOrder()会为你投(Comparator<T>),所以你不必自己动手。Comparator<MyObject> myComparator = ReverseComparator.REVERSE_ORDER; (同样,如果您没有发现此问题,那是因为您没有启用足够的警告,这意味着您需要重新考虑您的做法。)

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

Comparator<MyObject> myComparator = 
    (Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;

你会收到一个错误,因为这是一个无效的作业。所以,您必须将其更改为:

@SuppressWarnings( "unchecked" )
Comparator<MyObject> myComparator = 
    (Comparator<MyObject>)ReverseComparator.REVERSE_ORDER;

然后你会得到一个警告,因为这是一个未经检查的演员。所以你最终不得不这样做:

Collections.reverseOrder()

这很难看。因此,Comparator<MyObject> myComparator = Collections.reverseOrder(); 可以帮助您完成此操作:

Comparator<String>
  

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

好的,我知道你真正的问题是什么。欢迎来到java泛型和类型擦除的精彩世界。我会尝试解释,但一定要查找术语&#34;键入擦除&#34;为了充分理解这个概念。

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

因此,在java中,如果您真的知道自己在做什么,则可以自由地将泛型类的实例强制转换为同一个类的实例,但使用不同的泛型参数。在这种情况下,java运行时的创建者正在向Comparator<T>投射Comparator<Integer>,事实上这可能会被分配到@SuppressWarnings( "unchecked" ),这很好。

这个演员虽然很棘手,因为编译器必须相信你真的知道自己在做什么,所以默认情况下,编译器发出一个&#34;未经检查的赋值&#34;对这些演员表示警告,然后我们表示我们发誓我们知道我们正在使用Collections.reverseOrder()注释做什么。

handle让你不必担心这一切。

答案 1 :(得分:2)

这就是关于类型擦除的全部内容。请记住,在运行时没有Comparable<Object>这样的东西,只有Comparable这样的东西。因此,compare的{​​{1}}方法适用于两个REVERSE_COMPARATOR个实例。它不会在运行时导致String因为ClassCastException实现String,而在运行时只会Comparable<String>

但是,方法Comparable必须是通用的,否则用户必须先将返回的对象强制转换为适当的类型才能使用它。例如,请考虑此代码,其中比较器的类型与声明的reverseComparator类型相同。

REVERSE_COMPARATOR

这不编译的原因是因为Comparator<Comparable<Object>> comparator = Collections.reverseOrder(); String[] arr = {"A", "B", "C"}; Arrays.sort(arr, comparator); // Doesn't compile. arr数组,因此String需要Arrays.sort,但Comparator<? super String>是不是Comparable<Object>Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?)的超类型。

您可以使用强制转换进行编译:

Comparable<String>

这会产生一个警告,但正如您将看到的那样,您可以尝试它,它可以正常工作。通过使用通用方法,使用该方法将演员的丑陋和警告排除在代码之外。

同一个对象(Comparator<Comparable<Object>> comparator = Collections.reverseOrder(); String[] arr = {"A", "B", "C"}; Arrays.sort(arr, (Comparator<String>) (Object) comparator); System.out.println(Arrays.toString(arr)); // prints [C, B, A] )可以被视为REVERSE_COMPARATORComparator<String>,或Comparator<Integer> Comparator<X>是{类型实现X是类型擦除的众多好处之一。在C#中无法重用对象,因为在泛型类的C#实例中知道类型。

有很多这种重用的例子。例如,无论您提供什么样的泛型类型,所有这些泛型方法总是返回相同的实例。

Comparable