Java自定义Collector使用V extends Collection泛型报Unexpected Bounds错误
错误原因
直接在Map<String, V extends Collection<String>>的类型参数位置声明泛型边界不符合Java泛型语法规范:泛型类型参数的边界声明必须放在类/方法的泛型参数定义区(即类名、方法名后的尖括号内),不能直接写在参数化类型的实参位置,这是编译器抛出Unexpected Bounds错误的核心原因。
除此之外,原实现中accumulator、combiner方法里硬编码new HashSet<>()的逻辑已经锁死了值集合的类型,就算泛型声明修正,也无法适配List类型的值容器。
可适配List/Set的实现方案
给Collector类声明泛型参数,将值集合的构造逻辑抽为可传入的Supplier,即可实现不同Collection子类的灵活适配,完整代码如下:
import java.util.*; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; public class MultiMapCollector<V extends Collection<String>> implements Collector<Entity, Map<String, V>, Map<String, V>> { private final Supplier<V> valueCollectionSupplier; // 构造时传入目标值集合的构造方法引用 public MultiMapCollector(Supplier<V> valueCollectionSupplier) { this.valueCollectionSupplier = valueCollectionSupplier; } @Override public Supplier<Map<String, V>> supplier() { return HashMap::new; } @Override public BiConsumer<Map<String, V>, Entity> accumulator() { return (map, e) -> map.computeIfAbsent(e.getName(), k -> valueCollectionSupplier.get()).add(e.getValue()); } @Override public BinaryOperator<Map<String, V>> combiner() { return (map1, map2) -> { map2.forEach((key, valueCol) -> map1.computeIfAbsent(key, k -> valueCollectionSupplier.get()) .addAll(valueCol)); return map1; }; } @Override public Function<Map<String, V>, Map<String, V>> finisher() { return Collections::unmodifiableMap; } @Override public Set<Characteristics> characteristics() { // 值为有序集合时不要返回UNORDERED特性,避免并行流打乱顺序 return Collections.emptySet(); } // 静态工厂方法,简化常用场景调用 public static MultiMapCollector<Set<String>> toSetMultimap() { return new MultiMapCollector<>(HashSet::new); } public static MultiMapCollector<List<String>> toListMultimap() { return new MultiMapCollector<>(ArrayList::new); } }
使用示例
// 收集为值类型是HashSet的Multimap Map<String, Set<String>> setMultimap = entityList.stream() .collect(MultiMapCollector.toSetMultimap()); // 收集为值类型是ArrayList的Multimap Map<String, List<String>> listMultimap = entityList.stream() .collect(MultiMapCollector.toListMultimap()); // 自定义值集合类型,比如LinkedHashSet Map<String, LinkedHashSet<String>> customMultimap = entityList.stream() .collect(new MultiMapCollector<>(LinkedHashSet::new));
注意事项
- 所有自定义泛型类型参数必须先在类/方法定义的泛型参数列表中声明,禁止在参数化类型的实参位置直接写
V extends Xxx格式的边界声明 - 不要硬编码集合实现类,通过传入的
Supplier动态构造值集合,才能保证不同Collection类型的适配性 - 使用List、LinkedHashSet等保序集合作为值容器时,不要添加
Characteristics.UNORDERED特性,否则并行流收集时可能破坏元素的预期顺序
内容的提问来源于stack exchange,提问作者ng.newbie
相关产品推荐
相关产品推荐

