(以下不太理解,只是照部分能看懂的搬,不作为参考,只作为笔记)
擦拭法
所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。
例如,我们编写了一个泛型类Pair,但是虚拟机根本不知道泛型,因此造成了两个局限性:
<T>
不能是基本类型,例如int,因为虚拟机看到的实际类型应该是Object类,object类无法持有基本类型。(除了八大基本数据类型外,其余的类型都是Object的子类)- 无法取得带泛型的class。当创建实例通过反射获取其class时,获取到的都是泛型的类型,例如:
public class Pair<T> {}
中获取到的是T类型。 - 无法判断带泛型的类型:
if (p instanceof Pair<String>) {}
原因与上一样。 - 不能实例化T类型:
public class Pair<T> {
private T first;
private T last;
public Pair() {
// Compile error:
first = new T();
last = new T();
}
}
这样的代码编译后会变成这样:
first = new Object();
last = new Object();
创建实例就变成了Object类型
想要成功创建实例,则需要利用反射:
public class Pair<T> {
private T first;
private T last;
public Pair(Class<T> clazz) {
first = clazz.newInstance();
last = clazz.newInstance();
}
}
泛型继承:子类可以继承父类的泛型类
extends通配符
在泛型中,方法传递参数时,若方法传递需要的是一个pair<Number>
类型,而我们传递过去的是一个pair<Integer>
类型的参数,会出现异常。
因此解决此异常需要用到extends通配符,向上转型
如下demo:
public static void main(String[] args) {
Pair<Integer> p = new Pair<>(123, 456);
int n = add(p);
System.out.println(n);
}
static int add(Pair<? extends Number> p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
可以用extends限定泛型类的类型
public class Pair<T extends Number> { ... }
将T限定在Number中。
super通配符
与extends相反,super通配符是向下转型
public static void main(String[] args) {
Pair<Number> p1 = new Pair<>(12.3, 4.56);
Pair<Integer> p2 = new Pair<>(123, 456);
setSame(p1, 100);
setSame(p2, 200);
System.out.println(p1.getFirst() + ", " + p1.getLast());
System.out.println(p2.getFirst() + ", " + p2.getLast());
}
static void setSame(Pair<? super Integer> p, Integer n) {
p.setFirst(n);
p.setLast(n);
}
使用<? super Integer>通配符作为方法参数,表示方法内部代码对于参数只能写,不能读。
二者对比:
- <? extends T>允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
- <? super T>允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。
PECS原则
PECS原则:Producer Extends Consumer Super。方便记忆何时使用extends或super
无限通配符
无限定通配符<?>很少使用,可以用<T>
替换,同时它是所有<T>
类型的超类。
泛型和反射
- 部分反射API是泛型,例如:Class,Constructor;
- 可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型;
- 可以通过Array.newInstance(Class, int)创建T[]数组,需要强制转型;