我试图了解JDK下游减少的实现.就这个:
public static >
Collector groupingBy(Function super T, ? extends K> classifier,
Supplier mapFactory,
Collector super T, A, D> downstream) {
Supplier downstreamSupplier = downstream.supplier();
BiConsumer downstreamAccumulator = downstream.accumulator();
BiConsumer, T> accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(container, t);
};
BinaryOperator> merger = Collectors.>mapMerger(downstream.combiner());
@SuppressWarnings("unchecked")
Supplier> mangledFactory = (Supplier>) mapFactory;
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
}
else {
@SuppressWarnings("unchecked")
Function downstreamFinisher =
(Function) downstream.finisher(); //1,
Function, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
}
}
在// 1处,下游燃烧器的类型为功能< A,D>.通过类型参数声明判断< T,K,D,A,M扩展Map< K,D>>,类型D不依赖于A.那么我们为什么要将它转换为函数< A,A>.我认为,类型D甚至可能不是A的子类.
我错过了什么?
解决方法:
如果下游收集器没有身份修整器并且修整器功能返回与中间容器类型不同的类型,则此收集器实际上违反了Map的通用类型安全性.
在收集操作期间,映射将保存类型A的对象,即中间容器类型.然后,在操作结束时,groupingBy的修整器将遍历地图并将修整器功能应用于每个值并将其替换为最终结果.
当然,如果没有未经检查的操作,这将无法实现.有多种方法,您发布的变体,从供应商< M>中更改地图供应商的类型.到供应商< Map< K,A>> (第一个未经检查的操作),因此编译器接受该映射将保存类型A而不是D的值.这就是为什么必须将整理器函数更改为函数< A,A> (第二个未经检查的操作),所以它可以在map的replaceAll操作中使用,它似乎需要类型为A的对象,尽管它实际上是D.最后,必须将结果映射转换为M(第三个未经检查的操作)以获取对象供应商实际提供的预期结果类型M的结果.
正确的类型安全替代方案是使用不同的映射并通过使用转换中间映射的值的结果填充结果映射来执行整理操作.这不仅可能是昂贵的操作,还需要第二个供应商用于中间地图,因为提供的供应商仅生成适合最终结果的地图.显然,开发人员认为这是一种可接受的类型安全漏洞.
请注意,当您尝试使用强制类型安全的Map实现时,您会注意到不安全的操作:
Stream.of("foo", "bar").collect(Collectors.groupingBy(String::length,
() -> Collections.checkedMap(new HashMap<>(), Integer.class, Long.class),
Collectors.counting()));
将使用此实现生成ClassCastException,因为它尝试将中间容器(数组)而不是Long放入Map中.来源:http://www.icode9.com/content-1-231701.html