我有以下
Comparator
实现:
private static final class ValueComparator<Value_, Point_ extends Comparable<Point_>> implements Comparator<Value_> {
private final Function<Value_, Point_> indexFunction;
public ValueComparator(Function<Value_, Point_> indexFunction) {
this.indexFunction = Objects.requireNonNull(indexFunction);
}
@Override
public int compare(Value_ o1, Value_ o2) {
if (o1 == o2) {
return 0;
}
Point_ point1 = indexFunction.apply(o1);
Point_ point2 = indexFunction.apply(o2);
if (point1 == point2) {
return 0;
}
int comparison = point1.compareTo(point2);
if (comparison != 0) {
return comparison;
}
// Identity Hashcode for duplicate protection; we must always include duplicates.
// Ex: two different games on the same time slot
int identityHashCode1 = System.identityHashCode(o1);
int identityHashCode2 = System.identityHashCode(o2);
return Integer.compare(identityHashCode1, identityHashCode2);
}
}
这段代码运行了一段时间,然后突然抛出一个 NPE -
indexFunction
不知何故变成了 null
,即使它在构造函数中被检查为非空。
显然,这让我很烦恼,所以我去查找,发现
indexFunction
是 Math::abs
,对静态方法的引用。 java.lang.Math.abs(int)
是 @HotSpotIntrinsicCandidate
。
这是否意味着 HotSpot 在某些时候内在化了方法,因此方法引用变得无效?我该如何保护自己免受此类事情的侵害?
$ java -version
openjdk version "11.0.12" 2021-07-20
OpenJDK Runtime Environment Temurin-11.0.12+7 (build 11.0.12+7)
OpenJDK 64-Bit Server VM Temurin-11.0.12+7 (build 11.0.12+7, mixed mode)
事实证明答案要简单得多,而且也不那么耸人听闻。
indexFunction
的参数为空,并且函数期望 int
,NPE 来自拆箱。在这种情况下,NPE 仍然指向 indexFunction
,而不是其中的某些代码,这最初使我走上了完全错误的道路。
显然我并没有像我想象的那样彻底检查我的输入。菜鸟错误,案件已结。
你遇到了一个错误,看到了一些你不理解的东西,而且,显然你已经无计可施,无法理解你的代码如何受到指责,放大了这个你不理解的东西:一定是它!
通常这不是一个很好的调试方法。 Java bug 或设计白痴解释你的问题的可能性是无限小的(因为说实话,如果
final
变量随机消失在稀薄的空气中,要么以某种方式变成 null
,或者更糟糕的是,保持原来的样子,但任何尝试与它交互会导致“哦,函数消失了”,而这是由 NullPointerException 处理的 - 这很愚蠢)。这是使用拥有众多用户的语言的优势之一。你是第一个遇到它的几率非常低。
我知道,由于现已确认的 JVM 错误,我曾在一次 Google Code Jam 挑战赛中失败。所以它可能发生。但可能性是无限小的。
这是否意味着 HotSpot 在某些时候内在化了方法,因此方法引用变得无效?不。那是不可能的。这不是“内在”的含义或含义。
我该如何保护自己免受此类事情的侵害?
你不能,这很好,因为你不必这样做 - 这永远不会发生。
检查您的堆栈跟踪。
indexFunction
不为空。然而,索引函数可能会抛出 NPE,如果
o1
为空且 o2
不为空,则可能会抛出 NPE,反之亦然。另外一点,你为什么要编写这段代码?这已经存在于 JVM 中:而不是打电话:
new ValueComparator(x -> x.getPoint());
您只需拨打:
Comparator.comparing(x -> x.getPoint()).thenComparingInt(System::identityHashCode);