Java嵌套泛型(super与extends混合)赋值问题及有效构造咨询
先明确几个核心规则:Java泛型默认是不变的(即不带通配符时,泛型类型必须严格匹配);? extends T是协变(允许子类型泛型赋值给父类型泛型容器);? super T是逆变(允许父类型泛型赋值给子类型泛型容器)。
为什么l1的赋值有效?
看这段代码:
List<? extends ArrayList<? extends Integer>> l1 = new ArrayList<ArrayList<Integer>>();
左侧的泛型是? extends ArrayList<? extends Integer>,表示这个List的元素可以是ArrayList<? extends Integer>或者它的任何子类型。右侧的ArrayList<Integer>是ArrayList<? extends Integer>的子类型(因为Integer是? extends Integer的具体实现),所以ArrayList<ArrayList<Integer>>完全符合左侧? extends ArrayList<? extends Integer>的协变要求,再加上ArrayList是List的实现类,因此这个赋值完全合法。
为什么l2和l3会报错?
关于l2的错误
代码:
List<ArrayList<? extends Integer>> l2 = new ArrayList<ArrayList<Integer>>();
左侧是无通配符的严格泛型,要求右侧的泛型参数必须和左侧完全一致。这里左侧的泛型参数是ArrayList<? extends Integer>,而右侧是ArrayList<Integer>——前者是后者的父类型,但泛型在无通配符时是不变的,不能把子类型泛型的容器赋值给父类型泛型的容器,因此编译器判定为不兼容类型。
关于l3的错误
代码:
List<? super ArrayList<? extends Integer>> l3 = new ArrayList<ArrayList<Integer>>();
左侧的? super ArrayList<? extends Integer>是逆变通配符,要求右侧的泛型参数必须是ArrayList<? extends Integer>的父类型。但右侧的ArrayList<Integer>是ArrayList<? extends Integer>的子类型,不符合逆变的要求,所以报错。
l2和l3的有效构造方式
构造合法的l2
只需要让右侧的泛型参数和左侧完全一致即可,示例:
// 显式指定泛型参数 List<ArrayList<? extends Integer>> l2 = new ArrayList<ArrayList<? extends Integer>>(); // Java 7+ 可用菱形语法自动推断 List<ArrayList<? extends Integer>> l2 = new ArrayList<>(); // 也可以直接初始化包含符合要求的元素 List<ArrayList<? extends Integer>> l2 = new ArrayList<>(List.of(new ArrayList<Integer>()));
构造合法的l3
需要让右侧的泛型参数是ArrayList<? extends Integer>的父类型,比如List<? extends Integer>、Collection<? extends Integer>甚至Object,示例:
// 用List<? extends Integer>作为泛型参数(ArrayList是List的子类) List<? super ArrayList<? extends Integer>> l3 = new ArrayList<List<? extends Integer>>(); // 用更上层的Collection<? extends Integer> List<? super ArrayList<? extends Integer>> l3 = new ArrayList<Collection<? extends Integer>>(); // 最宽泛的用Object List<? super ArrayList<? extends Integer>> l3 = new ArrayList<Object>(); // 菱形语法会自动推断为ArrayList<Object>,也合法 List<? super ArrayList<? extends Integer>> l3 = new ArrayList<>();
内容的提问来源于stack exchange,提问作者vortex

