考虑来自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
。
答案 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_COMPARATOR
或Comparator<String>
,或Comparator<Integer>
Comparator<X>
是{类型实现X
是类型擦除的众多好处之一。在C#中无法重用对象,因为在泛型类的C#实例中知道类型。
有很多这种重用的例子。例如,无论您提供什么样的泛型类型,所有这些泛型方法总是返回相同的实例。
Comparable