略,只需注意 Java 泛型是编译擦除机制,只在编译时有效,运行时无效。
完全个人总结,征求来源认证
泛型说到本质,是模板替换,需遵循模板替换的基本原则,需要涉及到下述三种标记。
泛型参数(模板变量)
标记替换什么,即替换内容的含义是什么。这是额外的独立定义,在 Javadoc 会有单独位置,只有类和方法旁边才能定义泛型参数。
举例:
public interface List<E></font> { … } // List 后面的<E>
<T> T[] toArray(T[] a) {} // 最前面的<T>
注意点: 这是个参数声明,因此请勿使用“ ?”。
泛型(模板变量的引用)
标记在哪里替换。这是定义要替换的位置,因此它必定是替换掉之前的位置。类当中的类型声明,例如方法的形参类型,方法返回类型,可被替换为泛型。变量的类型声明,不属于类,故不参与泛型,而是参与后文所述的定型。
举例:
public interface List<E> { boolean add(E e); } // add 方法的参数类型 E
<T> T[] toArray(T[] a) {} // 参数类型 T , 还有 返回类型 T[]
注意点:
Class<T> { List<T> getList(List<T> paramList); }
,两个List<T>
中这个 T , 在 Class 类中是泛型 ,但同时,也是对List<E>
的 泛型参数 E 的定型。
定型(模板变量的值)
标记替换成什么。_模板变量的值可能无需刻意定义,只要能根据上下文推断出来即可_
举例:
List<String> = new ArrayList<String>() // 显式定义
Optional.of("123").orElseGet(()->"sss"); // 上下文自动推断
List<String> = new ArrayList<>(); // 显式定义+上下文自动推断
List<? extends B> bList= new ArrayList<A>; // 向上造型通配符
List<? super A> bList = new AyyarList<B>; // 向下造型通配符
关于造型通配符,见后文
__ <? extends SomeObject>
,跟 <T extend SomeObject>
,是两码事: __
<T extends SomeObject>
是泛型参数,它将 T 的可选范围,从 Object 缩小 为 SomeObject 。<T super SomeObject>
<? extends/super SomeObject>
是通配符定型,它以 SomeObject 的向上 /向下造型通配,作为定型。<? extends/super T>
。关于造型通配符,见后文
特别说明: Java 泛型是 1.5 加入,并且加入之后再没有动过,这里的笔记也是 Java 6 年代总结的,那时候 C# 泛型可能还没出或者出来了但没流行。所以这里没有 PECS ,没有斜变逆变,这俩理论可以作为外部参考,但是它们不太可能是 Java 泛型的基础原理。
由于类型参数与类型声明无关,因此类型参数的继承关系不能区分泛型实例的继承关系。例如List<Integer>
与List<Object>
之间没有继承关系,他们都是 List 接口的实例并且由于加入了类型参数限定二者之间也不能做强制类型转换。而自然情况以及面向对象的多态型又是需要该区分的即List<Integer>
是List<Object>
的子类,此时需要使用“? extends”、“? super”通配符,但是注意这只是一种变通手段,并且是不完善的。毕竟泛型是 编译时 的特征,而多态是 运行时 的特征。
提示: 下文均假设 A extends B 。
** 可以使用其“方法返回类型限定”的方法(例如 get 方法),不能使用其“方法参数类型限定”的方法(例如 add 、set 方法)。 **
举例:
List< ? extends B> bList= new AyyarList<A>;
B b = bList.get(0); // 正确
bList.add(new A()); // 编译错误
分析:
<? extends B>
可自动 向上造型 到 B ,符合要求。允许实际使用此方式。<? extends B>
完全无法限定具体类型是 A ,故若使用“方法参数类型限定”的方法,则强制编译不通过。不允许实际使用此方式。可以使用其“方法参数类型限定”的方法(例如 add 、set 方法),不过参数类型应当是类型参数或其子类型(而不是父类);仍然可以使用其“方法返回类型限定”的方法(例如 get 方法),但只能得到 Object 类型的返回类型。
举例:
List<? super A> bList = new AyyarList<B>;
bList.add(new A()); // 正确
bList.add(new B()); // 编译警告,但不是编译错误,
Object obj = bList.get(0); // 可用,但是没有实际意义
分析:
List bList = new ArrayList<B>
,仍然是多态,但不是常规多态,故没有向上造型通配符用得多。<? super A>
额外要求参数类型是 A (即虽然是 bList ,但额外要求参数是它的 向下造型 A )。实际使用中,如果传入参数类型是 A ,符合要求。如果是 B ,则有可能符合要求(实例正好是 A 类型),也有可能是不符合要求,但又没错(毕竟实际类型,即上面语句中等号的右边,是 List<B>
),故此时会给编译警告。允许实际使用此方式,但应当先向下造型再给参数。<? super A>
的父类只能推断成最高超类 Object 。允许但不推荐使用此方式。? extends
通配符? super
通配符 1
nothingistrue OP |
2
fredli 2022-06-14 10:26:52 +08:00
明白类型擦除,很多都理解了啊
|
3
siweipancc 2022-06-14 11:13:48 +08:00 via iPhone
我是看 oracle 教程的,简单明了
|