Guava的Iterables.concat不支持直接传入Iterable<? extends T>数组,但可通过传入具体子类型集合(如Iterable<Integer>和Iterable<Double>)让编译器自动推断共同上界T=Number,安全拼接为Iterable<Number>。

Guava 的 Iterables.concat 本身不直接支持上界通配符(? extends T)用于“平铺多个不同子类型集合”,但你可以通过类型擦除规则和合理的泛型边界设计,安全地将多个 Iterable<? extends T> 拼接成一个 Iterable<T>。关键在于:**不要试图把 Iterables.concat 的参数声明为 Iterable<? extends T> 数组或列表,而是让每个参数独立推断为 Iterable<X>,其中 X 是 T 的子类型——Java 泛型会自动向上转型为 Iterable<T>**。
为什么不能直接传 Iterable<? extends T>[]?
Iterables.concat 是可变参数方法:public static <T> Iterable<T> concat(Iterable<T>... inputs)。它的形参是 Iterable<T>...,不是 Iterable<? extends T>...。如果你强行传入 Iterable<String> 和 Iterable<Integer> 给同一个 T,编译器无法统一 T 类型(因为 String 和 Integer 没有共同非 Object 的父类)。但如果你传的都是 Iterable<? extends Number>,比如 Iterable<Integer> 和 Iterable<Double>,就可以统一为 T = Number:
-
Iterable<Integer>是Iterable<? extends Number>的具体实例,能赋值给Iterable<Number>(协变) - 所以
Iterables.concat(ints, doubles)可以成功推断T = Number,返回Iterable<Number>
正确写法:显式指定类型参数 + 用具体子类型集合
不依赖通配符变量,而是直接传入具体类型的 Iterable,让类型推断自然工作:
List<Integer> ints = Arrays.asList(1, 2);
List<Double> doubles = Arrays.asList(3.14, 2.71);
// ✅ 正确:编译器推断 T = Number
Iterable<Number> combined = Iterables.concat(ints, doubles);
for (Number n : combined) {
System.out.println(n); // 输出 1, 2, 3.14, 2.71
}
注意:ints 和 doubles 本身不是 Iterable<? extends Number> 类型变量,而是具体类型;但它们在调用时被视作 Iterable<Number> 的兼容实参。
立即学习“Java免费学习笔记(深入)”;
如果必须用通配符容器(如 List<Iterable<? extends T>>),需手动 cast 或辅助方法
Java 不允许将 List<Iterable<? extends Number>> 直接转为 Iterable<Number>...。此时需借助工具方法绕过泛型限制(不推荐,但可行):
- 方案一:用
FluentIterable+append链式调用(更安全) - 方案二:写一个泛型桥接方法,利用类型擦除做 unchecked cast(需加
@SuppressWarnings("unchecked"))
例如:
public static <T> Iterable<T> concatExtends(List<Iterable<? extends T>> sources) {
@SuppressWarnings("unchecked")
Iterable<T>[] array = sources.toArray(new Iterable[0]);
return Iterables.concat(array);
}
调用:concatExtends(Arrays.asList(ints, doubles)) → 返回 Iterable<Number>。本质是信任调用方传入的确实是 ? extends T,避免运行时 ClassCastException。
常见错误与规避
❌ 错误写法:Iterable<? extends Number> a = ints; Iterable<? extends Number> b = doubles; Iterables.concat(a, b);
原因:此时 a 和 b 类型都是 Iterable<? extends Number>,但通配符是独立的,编译器无法统一为同一 T,推断失败。
✅ 正确思路:让参数保持具体类型,或统一提升到共同上界再传入。


















