提问人:GreyBit 提问时间:10/13/2023 最后编辑:GreyBit 更新时间:10/14/2023 访问量:99
如果 c 不为 null,Arrays.sort(T[] a, Comparator<? super T> c) 可以抛出 ClassCastException 吗?
Can Arrays.sort(T[] a, Comparator<? super T> c) ever throw ClassCastException if c is not null?
问:
Java 20 API 文档标准库中的包中的 javadoc 如下所示:Arrays.sort(T[] a, Comparator<? super T> c)
java.util
public static void sort(T[] a, 比较器<? super T> c) 根据指定比较器引起的顺序对指定的对象数组进行排序。数组中的所有元素必须通过指定的比较器相互比较(即,c.compare(e1, e2) 不得为数组中的任何元素 e1 和 e2 引发 ClassCastException)。
这种排序是稳定的:相等的元素不会因为排序而重新排序。
实现说明:此实现是一种稳定的、自适应的、迭代的合并排序,当输入数组被部分排序时,它需要的比较远远少于 n lg(n),而当输入数组被随机排序时,它提供了传统合并排序的性能。如果输入数组几乎已排序,则实现需要大约 n 次比较。临时存储要求各不相同,从几乎排序的输入数组的小常量到随机排序输入数组的 n/2 个对象引用。
该实现在其输入数组中利用升序和降序的同等优势,并且可以利用同一输入数组不同部分的升序和降序。它非常适合合并两个或多个排序数组:只需连接数组并对生成的数组进行排序即可。
该实现改编自 Tim Peters 的 Python 列表排序 ( TimSort)。它使用了 Peter McIlroy 的“Optimistic Sorting and Information Theoretic Complexity”中的技术,收录于第四届年度 ACM-SIAM 离散算法研讨会论文集,第 467-474 页,1993 年 1 月。
类型参数: T - 要排序的对象的类 参数: a - 要排序的数组 c - 用于确定数组顺序的比较器。null 值表示应使用元素的自然顺序。 抛出: ClassCastException - 如果数组包含使用指定的比较器不能相互比较的元素 IllegalArgumentException - (可选)如果发现比较器违反了比较器合约
强调我的。
编译器难道不能保证(通过使用类型系统和方法的泛型签名)的所有元素实际上都是相互比较的,并且只要不为空,就不会抛出 ?a
c
c.compare(e1, e2)
ClassCastException
c
据推测,删除类型参数的 的“实际”签名将如下所示: 的签名(假设在我们的程序中具有静态类型)将如下所示: 并且在其实现中包含两个强制转换: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;
//...
当且仅当运行时类型为 and 或 的子类型时,这些强制转换才会成功。o1
o2
Number
Number
和的运行时类型是 的 或 的子类型,当且仅当 的运行时类型是 或 的子类型,例如 。o1
o2
Number
Number
a
Number[]
Number[]
Integer[]
的运行时类型是 的 或 的子类型,当且仅当 的静态类型为 或 的子类型。(如果 的运行时类型不是 IS 或子类型不是,但静态类型为 IS,则在输入 body 之前会抛出 a)a
Number[]
Number[]
a
Number[]
Number[]
a
Number[]
Number[]
a
ClassCastException
Arrays.sort
的静态类型是 的 要么是 的子类型,是因为 的静态类型是 ( 是 so 是 要么是 的子类型 )。a
Number[]
Number[]
static <T> void sort(T[] a, Comparator<? super T> c)
c
Number
? super T
Number
T
Number
Number
我写了一些测试来尝试抛出一个,并且只有在通过而不是实际的比较器时才真正得到它。Arrays.sort
ClassCastException
null
c
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
:传递静态类型既不是 to 也不是子类型的数组是编译错误。
取消注释该测试并尝试编译会为我产生以下结果:Number[]
Number[]
sort
TestToArray.java:46:错误:未找到适合排序的方法(对象[],比较器) Arrays.sort(数字, compareAsWholeNumber); ^ 方法 Arrays.<T#1>sort(T#1[],Comparator<? super T#1>) 不适用 (推理变量 T#1 具有不兼容的边界 上限:数字,对象 下限:对象) 方法 Arrays.<T#2>sort(T#2[],int,int,Comparator<? super T#2>) 不适用 (无法推断类型变量 T#2 (实际参数列表和正式参数列表的长度不同)) 其中 T#1,T#2 是类型变量: T#1 扩展了方法中声明的对象 <T#1>sort(T#1[],Comparator<? super T#1>) 中声明的对象 T#2 扩展了方法中声明的对象 <T#2>sort(T#2[],int,int,Comparator<? super T#2>)
sortingDisguisedStringsThrowsClassCastException
:顾名思义,这里确实抛出了 a,但这是误导性的:将堆栈跟踪打印到 std_out 会产生以下内容(为简洁起见,请减少):ClassCastException
java.lang.ClassCastException:class [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) 在 java.base/java.util.ArrayList.forEach(ArrayList.java:1511) <9 个内部行> 在 java.base/java.util.ArrayList.forEach(ArrayList.java:1511) <30 个内部行> 在 jdk.proxy1/jdk.proxy1.$Proxy 2.stop(来源不明)<7 行内行> 在 worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) 在 worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
请注意:1.失败的强制转换是从 to 而不是 from to,因为我们期望它适用于方法内部的失败强制转换,以及 2.是从类内部抛出的,而不是从类中抛出的。[Ljava.lang.Object
[Ljava.lang.Number
java.lang.Object
java.lang.Number
compare
ClassCastException
TestToArray
Arrays
测试表明,只需将数组分配给数组 就会抛出相同的 .sortingDisguisedStringsThrowsClassCastExceptionAtArrayAssignment
String[]
Number[]
ClassCastException
最后一个测试实际上抛出了预期的结果,这是我能够让方法本身抛出此异常的唯一情况。将堆栈跟踪打印到std_out将产生以下内容(为简洁起见,请减少):sortingNonComparableWithNullComparatorThrowsClassCastException
ClassCastException
sort
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.$Proxy 2.stop(来源不明)<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
null
c
上下文:我希望找出我需要犯什么样的错误才能使类型系统不再能够作为 的保护措施。到目前为止,编译器似乎至少会发出一个“未选中”的警告,这令人放心。有没有办法在不编译器发出“未检查”警告(并且不传递代替)的情况下获得一个?ClassCastException
sort
ClassCastException
sort
null
c
答:
如果你注意 的定义,那就是“[The Comparator] 不得抛出 ClassCastException”。实际上,您的任何测试都没有违反该规则。mutually comparable
class ArraysTest {
@Test
public void sortingWithBadComparitor() {
Number[] numbers = new Number[] { 4.0, 2, 3.0f };
Throwable e = Assertions.assertThrows(ClassCastException.class, () -> {
Arrays.sort(numbers, (o1, o2) -> {
throw new ClassCastException();
});
});
e.printStackTrace();
}
static final Comparator<Number> compareAsWholeNumberWithBug = new Comparator<Number>() {
@Override
public int compare(java.lang.Number o1, java.lang.Number o2) {
if (o1 instanceof Integer) { // Bug: assumes that if o1 is an integer, so is o2
return ((Integer) o1).intValue() - ((Integer) o2).intValue();
}
return Long.compare(o1.longValue(), o2.longValue());
}
};
@Test
public void sortingWithBuggyComparitor() {
Throwable e = Assertions.assertThrows(ClassCastException.class, () -> {
Arrays.sort(new Number[] { 4.0, 2, 3.0f }, compareAsWholeNumberWithBug);
});
e.printStackTrace();
}
}
您可能会反对,因为 sort 方法实际上并没有抛出一个新的 ,但在 Java 文档中的许多其他地方都是如此。该标记经常用于方法未捕获的实现中调用引发的异常。ClassCastException
@throws
评论
Comparator
ClassCastException
c.compare
NullpointerException
IndexOutOfBoundsException
Arrays
ClassCastException
如果您有原始类型(无论是否偶然),则触发异常非常简单:
class FawltyComparators {
@Test
void test() {
final Comparator numberComparer = new NumberComparator();
final Object[] array = { 1, 2, "3", "4" };
Arrays.sort(array, numberComparer);
}
private static final class NumberComparator implements Comparator<Number> {
@Override
public int compare(final Number o1, final Number o2) {
return 0; // doesn't matter
}
}
}
但这仍然会给你编译器警告:
FawltyCompators.java:17: warning: [unchecked] unchecked method invocation: method sort in class Arrays is applied to given types
Arrays.sort(array, numberComparer);
^
required: T[],Comparator<? super T>
found: Object[],Comparator
where T is a type-variable:
T extends Object declared in method <T>sort(T[],Comparator<? super T>)
FawltyCompators.java:17: warning: [unchecked] unchecked conversion
Arrays.sort(array, numberComparer);
^
required: Comparator<? super T>
found: Comparator
where T is a type-variable:
T extends Object declared in method <T>sort(T[],Comparator<? super T>)
评论
Arrays.sort();
评论
sort
ClassCastException
sort
(a, b) -> { throw new ClassCastException("Fake!"); }