传递左值/右值调用无界泛型函数的编译错误问题解析
直接传递Stream.collect结果触发类型推断失败的深层原因
首先看问题代码:
package org.example; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { public interface MyInt<E> { } public static final class MyNum<E> implements MyInt<E> { private E privados; } public static void f(List<? extends MyInt> l) { System.out.println("from List<?>"); } public static void main(String[] args) { var m = new MyNum(); List<MyNum> l = Arrays.asList(m); // 正常运行:先赋值给变量再传参 ArrayList<MyInt> r = l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)); f(r); // 编译失败:直接传递collect结果 f(l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new))); } }
现象差异的核心:类型推断的约束来源不同
1. 赋值给变量时的类型推断
当把collect结果赋值给ArrayList<MyInt> r时,编译器明确获取到目标类型:
- 变量类型
ArrayList<MyInt>直接指定了collect方法的返回类型必须是ArrayList<MyInt>。 - 编译器只需要验证:
ArrayList<MyInt>能否接收Stream<MyNum>的元素?因为MyNum是MyInt的子类,向上转型安全,所以collect的收集逻辑合法,编译通过。
2. 直接传参时的类型推断冲突
当直接把collect结果传给f(List<? extends MyInt> l)时,编译器需要同时满足两组约束:
约束一:匹配方法参数类型
collect的返回值必须是List<? extends MyInt>的子类型。
约束二:匹配Stream收集逻辑
Stream<MyNum>的元素是MyNum,所以collect的收集器必须能接收MyNum类型元素,这要求收集结果的集合类型满足Collection<? super MyNum>(因为Collector的泛型要求是? super T,T为MyNum)。
编译器试图推断一个同时满足这两组约束的类型,但这里出现了冲突:
List<? extends MyInt>的集合元素可以是任意MyInt子类(包括非MyNum的类型),而Collection<? super MyNum>允许接收MyNum及其父类,但泛型的不变性导致Collection<MyInt>(对应ArrayList<MyInt>)并不是Collection<MyNum>的子类型,反过来也不成立。- 编译器最终推断出一个不存在合法实现的交集类型
Object&List<? extends MyInt>&Collection<MyNum>,而这个类型无法同时满足所有上界约束,因此报错。
错误信息中&与,的含义区别
,:类型变量的多上界约束:错误里的upper bound(s): java.util.Collection<MyNum>,java.util.List<? extends MyInt>,java.lang.Object表示编译器要求推断的类型必须同时是这三个类型的子类型(即满足R extends Collection<MyNum> & List<? extends MyInt> & Object)。&:交集类型:推断出的java.lang.Object&java.util.List<? extends MyInt>&java.util.Collection<MyNum>是Java的隐式交集类型,代表一个对象同时属于这三个类型。但现实中没有集合类能同时满足Collection<MyNum>和List<? extends MyInt>的要求(泛型不变性导致两者无法兼容),因此这个类型不存在合法实现。
解决方法
除了先赋值给变量的方式,还可以显式指定collect的类型参数,直接告诉编译器返回类型:
f(l.stream().<ArrayList<MyInt>>collect(Collectors.toCollection(ArrayList<MyInt>::new)));
内容的提问来源于stack exchange,提问作者firas
相关产品推荐
相关产品推荐

