泛型的使用不多说了。详细谈谈常见通配符的区别:
- K V代表键值对
- E 代表Element
- T 某个具体的Java类型,在使用时需要被指定。
实际上具体是用KVET哪个字母,都一样,只是他们代表的东西是约定成成俗的,我用ABCDE可不可以呢,当然可以!
- ? 代表不确定 的Java类型。通配符,多用在函数的参数声明,不加限定就时是Object及其子类。
限定extends 和 super
- <? extends E>
必须是E的子类或者E本身,如果不是,编译失败
- <? super E>
必须是E或者E的父类,直到Object。E的子类不可以。
List<?> list; // 通配符,任何类都可以,实际没啥意义。
List<? extends Animal> list; // Animal和他的子类都可以
List<Animal> list; // 这个存入的时候只能是Animal,不可以是子类。
然而,T只有,E及其子类都可以。不能使用~~~~。因为T要在声明类的时候指定,这就和super本身的意思矛盾了。
?和T的区别
其实最本质的区别就是,无论在什么地方,在声明一个类或者借口的时候,T就变成了一个已知的类型。?是Object及其子类或者是用extends限定的,并不能在声明的时候去定义它。
-
T是确定的,通常是泛型类、泛型方法的定义。
-
?通配符,多用来在作为泛型方法的形参,用来声明变量没啥意义,因为他本身代表Object。
// 能保证是一个类型,因为在使用这个函数之前,T就是一个确定的东西,该函数中所有的T都是这个类型。
public <T extends Number> void test(List<T> dest, List<T> src)
//通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型
public void test(List<? extends Number> dest, List<? extends Number> src);
-
T 可以多重限定,?不行。
<T extends A & B> // 可以这么写,T是一个确定的类型,必须是A和B的公共子类或同时实现了这两个接口。 <? extends A & B> // 不可以。通配符不能多重限定。
泛型擦除
Java是伪泛型,即都是在编译的时候,所有的泛型信息都被擦除了,编译后的字节码不包括任何的泛型信息。这就导致在通过反射调用类型ArrayList的add方法,可以添加非Integer元素而不报错。
List<Integer> list = new ArrayList<>();
list.getClass().getMethod("add", Object.class).invoke(list, "abc");
因为编译之后擦出了Integer这个类型。如果没有使用extends限定,就是Object,否则就换成限定类型。
并且,会在结果字节码中插入强制类型转换,来将原始类型转换为泛型。
具体见参考2