为什么收藏者.tounmodimodiableelist使用中间累加器的类型检查?



我看到以下代码(JDK 16, JDK 17):

public static <T>
Collector<T, ?, List<T>> toUnmodifiableList() {
return new CollectorImpl<>(ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
list -> {
if (list.getClass() == ArrayList.class) { // ensure it's trusted
return SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArray(list.toArray());
} else {
throw new IllegalArgumentException();
}
},
CH_NOID);
}
JDK 16
  • JDK 17

使用if (list.getClass() == ArrayList.class) {检查的意义是什么?为什么不简单地让这段代码像

那样简单呢?
return new CollectorImpl<>(ArrayList::new, ArrayList::add,                                          
(left, right) -> { left.addAll(right); return left; },              
list -> SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(list.toArray()),                                                                  
CH_NOID);                                                           

通过将List::add替换为ArrayList::add,我们提供了类的最具体的方法句柄,而不是接口。最后的代码使用了一个假设,即累加器实例将作为ArrayList实例传递给'accumulator', 'combiner'和'finisher'参数(实际上,它会,因为CollectorImpl对所有参数使用相同的不变泛型变量A)。我看不出IllegalArgumentException和ClassCastException之间有什么区别,除了代码复杂性和结束器中的额外检查。

Collector<T, ?, List<T>> toUnmodifiableList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
list -> (List<T>)List.of(list.toArray()),
CH_NOID);
}

public static <T>
Collector<T, ?, List<T>> toUnmodifiableList() {
return new CollectorImpl<>(ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
list -> {
if (list.getClass() == ArrayList.class) { // ensure it's trusted
return SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArray(list.toArray());
} else {
throw new IllegalArgumentException();
}
},
CH_NOID);
}

分两步发生。中间的步骤是

Collector<T, ?, List<T>> toUnmodifiableList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
list -> (List<T>)SharedSecrets.getJavaUtilCollectionAccess()
.listFromTrustedArray(list.toArray()),
CH_NOID);
}

引入第一步是出于性能原因(https://bugs.openjdk.java.net/browse/JDK-8156071),后来有人注意到这会导致安全问题(https://bugs.openjdk.java.net/browse/JDK-8254090, https://github.com/openjdk/jdk/pull/449#issuecomment-704001706),因此进行了第二次更改。

最新更新