Java 20 API docs的标准库中的包java.util
中的Arrays.sort(T[] a, Comparator<? super T> c)
的javadoc代码如下:
公共静态空排序(T[]a,比较器?超级T>;c) 根据指定比较器产生的顺序对指定的对象数组进行排序.All elements in the array must be mutually comparable by the specified comparator (that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the array).
这种排序保证是稳定的:相等的元素不会因为这种排序而被重新排序.
实施说明:该实现是一种稳定的、自适应的迭代合并排序,当输入数组被部分排序时,所需的比较数远少于n个LG(N),而当输入数组被随机排序时,它提供了传统合并排序的性能.如果输入数组几乎已排序,则实现需要大约n个比较.临时存储要求从接近排序的输入数组的小常量到随机排序的输入数组的n/2对象引用不等.
该实现在其输入数组中平等地利用升序和降序,并且可以利用同一输入数组的不同部分的升序和降序.它非常适合合并两个或更多已排序的数组:只需连接数组并对结果数组进行排序.
该实现改编自Tim Peters的List Sort for Python(TimSort).它使用了Peter McIlroy在《第四届ACM-SIAM离散算法研讨会论文集》,第467-474页,1993年1月发表的《乐观排序和信息理论复杂性》中的技术.
类型参数: T-要排序的对象的类 参数: A-要排序的数组 C-用于确定数组顺序的比较器.空值表示应使用元素的自然排序. 投掷: ClassCastException - if the array contains elements that are not mutually comparable using the specified comparator个 IlLegalArgumentException-(可选),如果发现比较器违反了比较器约定
重点是我的.
编译器不是保证(通过使用类型系统和方法的泛型签名)a
的所有元素使用c
实际上是相互比较的,并且如果c
不为空,c.compare(e1, e2)
永远不会抛出ClassCastException
吗?
假定,删除类型参数后的"实际"签名toArray
将如下所示:static void sort(Object[] a, Comparator c)
,而c.compare
的签名(假设c
在我们的程序中具有静态类型Number
)将如下所示:int compare(Object o1, Object o2)
,并且在其实现中将包含两个到Number
的强制转换:
Number e1 = (Number) o1;
Number e2 = (Number) o2;
//...
当且仅当运行时类型o1
和o2
是Number
或子类型Number
时,这些强制转换才会成功.
当且仅当a
的运行时类型是Number[]
或子类型Number[]
(例如Integer[]
)时,o1
和o2
的运行时类型是Number
或子类型Number
.
当且仅当a
的静态类型是Number[]
或子类型Number[]
时,a
的运行时类型是Number[]
或子类型Number[]
.(如果a
的运行时类型是not Number[]
或子类型Number[]
,但静态类型a
是,则将抛出ClassCastException
before输入Arrays.sort
的正文)
静态类型a
要么是Number[]
,要么是Number[]
的子类型,这源于方法签名static <T> void sort(T[] a, Comparator<? super T> c)
,因为c
的静态类型是Number
(? super T
是Number
,因此T
要么是Number
,要么是Number
的子类型).
我写了一些测试,试图让Arrays.sort
抛出ClassCastException
,只有在c
通过null
而不是真正的比较器时才真正得到它.
package org.example.basics.generics.arrays;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Comparator;
class ArraysTest {
private static final Comparator<Number> compareAsWholeNumber = new Comparator<Number>() {
@Override
public int compare(java.lang.Number o1, java.lang.Number o2) {
return Long.compare(o1.longValue(), o2.longValue());
}
};
@Test
public void sortingIntsThrowsNoException() {
Integer[] ints = new Integer[]{4, 2, 3};
Assertions.assertDoesNotThrow(() -> {
Arrays.sort(ints, compareAsWholeNumber);
});
}
@Test
public void sortingDoublesThrowsNoException() {
Double[] doubles = new Double[]{4.0, 2.0, 3.0};
Assertions.assertDoesNotThrow(() -> {
Arrays.sort(doubles, compareAsWholeNumber);
});
}
@Test
public void sortingNumbersThrowsNoException() {
Number[] numbers = new Number[]{4.0, 2, 3.0f};
Assertions.assertDoesNotThrow(() -> {
Arrays.sort(numbers, compareAsWholeNumber);
});
}
// @Test
// public void sortingObjectsIsCompileError() {
// Object[] numbers = new Number[]{4.0, 2, 3.0f};
// Arrays.sort(numbers, compareAsWholeNumber);
// }
@Test
public void sortingDisguisedStringsThrowsClassCastException() {
Throwable e = Assertions.assertThrows(ClassCastException.class, () -> {
Arrays.sort((Number[]) new Object[]{"4.0", "2", "3.0f"}, compareAsWholeNumber);
});
e.printStackTrace();
}
@Test
public void sortingDisguisedStringsThrowsClassCastExceptionAtArrayAssignment() {
Assertions.assertThrows(ClassCastException.class, () -> {
Number[] numbers = (Number[]) new Object[]{"4.0", "2", "3.0f"};
});
}
@Test
public void sortingNonComparableWithNullComparatorThrowsClassCastException() {
Number[] numbers = new Number[]{4.0, 2, 3.0f};
Throwable e = Assertions.assertThrows(ClassCastException.class, () -> {
Arrays.sort(numbers, null);
});
e.printStackTrace();
}
}
上面的测试套件在我的机器上使用的是Eclipse Temurin 20.0.2成功运行.
特别要注意最后4次测试.
sortingObjectsIsCompileError
:传递静态类型既不是Number[]
也不是Number[]
到sort
的子类型的数组是编译错误.
取消对该测试的注释并try 编译将为我生成以下结果:
TestToArray.java:46:错误:找不到适合排序的方法(Object[],Compare ator) Arrays.sorte(Numbers,CompareAsWholeNumber); ^ 方法array.<;T#1&>排序(T#1[],比较器<;?Super T#1&>不适用 (推理变量T#1具有不兼容的边界 上限:数字、对象 下限:对象) 方法array.<;T#2&>排序(T#2[],int,int,比较器<;?Super T#2&>不适用 (无法推断类型变量(S)T#2 (实际参数列表和正式参数列表的长度不同) 其中T#1、T#2是类型变量: T#1扩展方法<;T#1&>Sort(T#1[],比较器<;?)中声明的对象超级T#1>;) T#2扩展方法<;T#2>;Sort(T#2[],int,int,Compare<;?)中声明的对象.超级T#2>;)
sortingDisguisedStringsThrowsClassCastException
:顾名思义,这里确实抛出了ClassCastException
,但这具有误导性:将堆栈跟踪输出到std_out会产生以下结果(为简洁起见进行了简化):
Java.lang.ClassCastException:类[Ljava.lang.Object;不能强制转换为类[Ljava.lang.Number;([Ljava.lang.Object;和[Ljava.lang.Number;都在加载器‘bootstrap’的模块java.base中]) 在org.example.basics.generics.arrays.TestToArray.lambda$sortingDisguisedStringsThrowsClassCastException$3(TestToArray.java:52)<;3条内部线路>; 在org.example.basics.generics.arrays.TestToArray.sortingDisguisedStringsThrowsClassCastException(TestToArray.java:51) AT java.base/java.util.ArrayList.forEach(ArrayList.java:1511)<;9条内部线路>; 在java.base/java.util.ArrayList.forEach(ArrayList.java:1511)<;30条内部线路>; 在jdk.proxy1/jdk.proxy1.$Proxy2.top(来源不详)<;7条内部线路>; 在worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) 在worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
请注意:1.失败的强制转换是从[Ljava.lang.Object
到[Ljava.lang.Number
,而不是我们预期的从java.lang.Object
到java.lang.Number
,因为它是针对compare
方法中的失败强制转换的;2.ClassCastException
是从TestToArray
类内部抛出的,not是从Arrays
类抛出的.
测试sortingDisguisedStringsThrowsClassCastExceptionAtArrayAssignment
显示,简单地将String[]
的数组赋给Number[]
的数组会抛出相同的ClassCastException
.
最后一个测试sortingNonComparableWithNullComparatorThrowsClassCastException
实际上抛出了预期的ClassCastException
,这是我唯一能够让sort
方法本身抛出此异常的情况.将堆栈跟踪打印到STD_OUT会产生以下结果(为简洁起见进行了简化):
Java.lang.ClassCastException:类java.lang.Double不能强制转换为类java.lang.Integer(java.lang.Double和java.lang.Integer在加载器‘bootstrap’的模块java.base中) 在java.base/java.lang.Integer.compareTo(Integer.java:72) 在java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320) 在java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188) 在java.base/java.util.Arrays.sort(Arrays.java:1041) 在java.base/java.util.Arrays.sort(Arrays.java:1228) 在org.example.basics.generics.arrays.TestToArray.lambda$sortingNonComparableWithNullComparatorThrowsClassCastException$5(TestToArray.java:67)<;30条内部线路>; 在jdk.proxy1/jdk.proxy1.$Proxy2.top(来源不详)<;7条内部线路>; 在worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) 在worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
注意,在本例中,失败的强制转换是从java.lang.Double到java.lang.Integer,而异常实际上是从Arrays
类抛出的.
Arrays.sort(T[] a, Comparator<? super T> c)
可以抛出ClassCastException
的唯一情况是为比较器c
传递值null
的情况吗?
背景:我希望找出我需要犯什么样的错误,才能使类型系统不再起到ClassCastException
对sort
的保护作用.到目前为止,编译器似乎至少会发出一个"未判断的"警告,这是令人放心的.有没有办法从sort
得到ClassCastException
,而不需要编译器发出"未经判断的"警告(并且不会用null
代替c
)?