对 Java 之泛型通配符 ?extends T 的认知 和 ? super T 一些理解

2022-06-11 00:22:38 +08:00
 Kaiv2

import java.util.ArrayList;
import java.util.List;

public class App {

    class A {

    }

    class B extends A {

    }

    class C extends B {

    }

    class D extends C {

    }

    public static void main(String[] args) {
        List<A> al = new ArrayList<A>();
        // 无法修改泛型继承关系
        al = new ArrayList<B>(); // 编译错误
        al.add(new A());
        al.add(new B());
        al.add(new C());
        A a = al.get(0);

        // 声明 List<? extends A> , 实现可能是 继承 A 的任意类型
        List<? extends A> al1 = new ArrayList<A>() {
            {
                // 放入子类
                add(new B());
            }
        };
        al1 = new ArrayList<B>() {

            {
                // 放入子类
                add(new C());
            }
        };
        al1 = new ArrayList<C>() {

            {
                // 无法放入 B
                add(new B()); // 编译错误
                // 放入子类
                add(new D());
            }
        };

        // 虽然编译器知道放入数据一定是继承 A
        // List<? extends A> al1 实现类一定是 List<A> 或 List<A 的子类>
        // 但是用户指定的实现不确定 比如 ArrayList<C> 无法放入 B 类型,会有类型转换错误
        al1.add(new A()); // 编译错误
        al1.add(new B()); // 编译错误

        // 因为声明的 List<? extends A>, 编译器确定类型一定是 A
        A x = al1.get(0);

        // 声明 List<? super D> , 实现可能是 D 的任意父类, 兼容放入 D 以及子类
        List<? super D> bl1 = new ArrayList<Object>() {
            {
                // 我放入和 D 类型无关的数据
                add("");
                add(new D());
            }
        };
        bl1 = new ArrayList<A>() {
            {
                add(new A());
                add(new D());
            }
        };

        // 编译器知道实现一定是 D 的父类
        // 支持放入任意 D 和 所有继承 D 的类
        // List<? super D> 声明了编译器只能放入 D 和 D 的实现类型
        bl1.add(new A()); // 编译失败
        bl1.add(new D());

        // 编译器无法获取确定实现类支持的数据类型,
        // 因为 List<? super D> bl1 可以是 List<D> 或 List<D 的父类> 的实现装载数据
        // 如果获取类型转换为 D 会有类型转换错误
        D b = bl1.get(0); // 编译错误
        Object o = bl1.get(0); // 所有类型的父类是 Object 所以支持
        // 需要人为强制转换, 有类型转换异常风险
        D b1 = (D) bl1.get(0);

    }
}


1487 次点击
所在节点    Java
1 条回复
WangYudi
2022-06-11 23:13:33 +08:00
感觉可以理解成继承的范围

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/858827

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX