为什么 Collectors.toUnmodifiableList 使用中间累加器的类型检查?
Why does Collectors.toUnmodifiableList use a type check of the intermediate accumulator?
我看到以下代码(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);
}
使用 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
,我们提供了 class 的最具体的方法句柄,而不是接口。
最后一段代码假设累加器实例将作为 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) and later some noticed that this leads to a security problem (https://bugs.openjdk.java.net/browse/JDK-8254090, https://github.com/openjdk/jdk/pull/449#issuecomment-704001706),因此进行了第二步更改。
我看到以下代码(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);
}
使用 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
,我们提供了 class 的最具体的方法句柄,而不是接口。
最后一段代码假设累加器实例将作为 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) and later some noticed that this leads to a security problem (https://bugs.openjdk.java.net/browse/JDK-8254090, https://github.com/openjdk/jdk/pull/449#issuecomment-704001706),因此进行了第二步更改。