Java泛型通配符实现安全集合拷贝:用? extends T读源集、? super T写目标集,避免类型错误;不可直接用List<Object>或原始类型,因泛型非协变;工具方法含空检查与类型推断,支持编译期安全。

<p>Java 中用泛型通配符实现安全的集合拷贝,核心是使用 <strong>? extends T</strong> 限定源集合类型,用 <strong>? super T</strong> 支持目标集合写入,避免运行时类型错误。</p>
<H3>为什么不能直接用 List<Object> 或原始类型</H3>
<p>原始类型(如 <font color="red">List</font>)丢失泛型信息,编译期无法校验元素类型;而 <font color="red">List<Object></font> 虽然“宽”,但无法接收 <font color="red">List<String></font> 等子类型——因为 <font color="red">List<String></font> 不是 <font color="red">List<Object></font> 的子类。泛型不是协变的,必须靠通配符桥接。</p>
<H3>安全拷贝方法签名设计</H3>
<p>理想签名应满足:源集合可读任意子类型,目标集合可写该类型或其父类。推荐如下:</p>
<p><strong>public static <T> void copy(List<? extends T> src, List<? super T> dest)</strong></p>
<ul>
<li><font color="blue">? extends T</font>:保证 src 中每个元素都能被当作 T 安全读取(例如 src 是 <font color="red">List<Integer></font>,T 可推为 <font color="red">Number</font> 或 <font color="red">Object</font>)</li>
<li><font color="blue">? super T</font>:保证 dest 能接受 T 类型的元素(例如 dest 是 <font color="red">List<Object></font> 或 <font color="red">List<Number></font>,都可 add(Integer))</li>
<li>类型参数 <strong><T></strong> 由编译器根据两个参数共同推断,确保读写类型兼容</li>
</ul>
<H3>完整工具方法实现</H3>
<p>支持空检查、避免副作用,并保留泛型安全性:</p>
<p><strong>public static <T> void copy(List<? extends T> src, List<? super T> dest) {<br>
if (src == null || dest == null) throw new NullPointerException();<br>
for (T item : src) {<br>
dest.add(item); // 编译器确认 item 类型兼容 dest<br>
}<br>
}</strong></p>
<p>调用示例:</p>
<ul>
<li><font color="red">copy(Arrays.asList(1, 2, 3), new ArrayList<Number>());</font> ✅(T 推为 <font color="red">Integer</font>,<font color="red">Integer</font> 是 <font color="red">Number</font> 子类)</li>
<li><font color="red">copy(Arrays.asList("a", "b"), new ArrayList<Object>());</font> ✅</li>
<li><font color="red">copy(new ArrayList<String>(), new ArrayList<Integer>());</font> ❌ 编译失败(String 无法赋给 Integer)</li>
</ul>
<H3>进阶:返回新集合的不可变拷贝</H3>
<p>若需创建副本而非追加,可封装为工厂方法:</p>
<p><strong>public static <T> List<T> copied(List<? extends T> src) {<br>
if (src == null) return Collections.emptyList();<br>
return new ArrayList<>(src); // 构造函数内部已做类型安全复制<br>
}</strong></p>
<p>注意:直接 <font color="red">new ArrayList<>(src)</font> 是安全的,因为 ArrayList 构造函数声明为 <font color="red">public ArrayList(Collection<? extends E> c)</font>,与我们的通配符逻辑一致。</p>