Eclipse与OpenJDK javac泛型通配符赋值行为差异及合规性疑问
问题描述
以下代码可在Eclipse 2022-12(4.26.0)中正常编译运行:
public static void main(String[] args) { List<List<?>> list = Arrays.asList(Arrays.asList("test")); System.out.println(test(list)); } public static <T> T test(List<? extends List<T>> list) { return list.get(0).get(0); }
但使用OpenJDK javac 19.0.2编译时会抛出编译错误:
error: method test in class Test cannot be applied to given types; System.out.println(test(list)); ^ required: List<? extends List<T>> found: List<List<?>> reason: cannot infer type-variable(s) T (argument mismatch; List<List<?>> cannot be converted to List<? extends List<T>>) where T is a type-variable: T extends Object declared in method <T>test(List<? extends List<T>>)
相关疑问:
- 哪个编译器的行为是正确的?
- 背后的原因是什么?
- Java语言规范(JLS)中是否有明确规定该转换是否允许?
- 补充实际场景:以下代码同样可在Eclipse中编译,但无法通过javac编译
ExecutorService executorService = /* ... */; List<Callable<?>> callables = /* ... */; executorService.invokeAll(callables);
解答
- javac的行为符合JLS规范,是正确的
- 核心原因:
- 问题本质是泛型类型推断与子类型转换的规则冲突。调用
test(list)时,编译器需要推断出一个确定的类型T,使得List<List<?>>可以安全转换为List<? extends List<T>>。 List<?>代表“元素类型未知的List”,而List<T>代表“元素类型为确定类型T的List”。List<?>并非List<T>的子类型——通配符?是一个不确定的类型占位符,而T是一个具体的类型变量,不存在任何确定的T能让List<?>成为List<T>的子类或超类。因此List<List<?>>无法满足List<? extends List<T>>的类型要求,因为前者的元素是“任意未知类型的List”,后者要求元素是“某个确定类型T的List的子类”,两者的类型边界不匹配。- Eclipse编译器做了超出规范的宽松处理,允许了不符合JLS的类型转换,而javac严格遵循了泛型的子类型规则。
- 问题本质是泛型类型推断与子类型转换的规则冲突。调用
- JLS的明确规定:
根据JLS第4.10.2节《泛型类和接口的子类型》,对于泛型类型G<C1>和G<C2>,只有当C1在泛型子类型规则下是C2的子类型时,G<C1>才是G<C2>的子类型。由于List<?>和List<T>之间不存在合法的子类型关系,因此List<List<?>>无法转换为List<? extends List<T>>。 - 实际场景的解释:
ExecutorService.invokeAll的方法签名为<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)。当传入List<Callable<?>>时,同样无法推断出确定的T让Callable<?>成为Callable<T>的子类型,因此javac拒绝编译,而Eclipse的宽松逻辑允许了该调用。
内容的提问来源于stack exchange,提问作者stonar96
相关产品推荐
相关产品推荐

