- 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List<?>.
无边界的通配符的主要作用就是让泛型能够接受未知类型的数据.
有一点我们必须明确, 我们不能对List<?>使用add方法, 仅有一个例外, 就是add(null). 为什么呢? 因为我们不确定该List的类型, 不知道add什么类型的数据才对, 只有null是所有引用数据类型都具有的元素. 请看下面代码:
public static void addTest(List<?> list) {
Object o = new Object();
// list.add(o); // 编译报错
// list.add(1); // 编译报错
// list.add("ABC"); // 编译报错
list.add(null);
}
由于我们根本不知道list会接受到具有什么样的泛型List, 所以除了null之外什么也不能add.
还有, List<?>也不能使用get方法, 只有Object类型是个例外. 原因也很简单, 因为我们不知道传入的List是什么泛型的, 所以无法接受得到的get, 但是Object是所有数据类型的父类, 所以只有接受他可以, 请看下面代码:
public static void getTest(List<?> list) {
// String s = list.get(0); // 编译报错
// Integer i = list.get(1); // 编译报错
Object o = list.get(2);
}
那位说了, 不是有强制类型转换么? 是有, 但是我们不知道会传入什么类型, 比如我们将其强转为String, 编译是通过了, 但是如果传入个Integer泛型的List, 一运行还会出错. 那位又说了, 那么保证传入的String类型的数据不就好了么? 那样是没问题了, 但是那还用<?>干嘛呀? 直接List不就行了.
补充(泛型的限定):
? extends E:接收E类型或者E的子类型。
?super E:接收E类型或者E的父类型。
使用场景:如果泛型的类型只在方法声明中出现一次,就可以用通配符<?>取代它。
总结:泛型通配符无需跟自定义泛型那样先声明在使用,?可以直接使用,无需声明,但是因为?是泛指不是具体的,所以在声明?的方法里面不能直接对?进行操作。
泛型的擦除:不要断章取义认为类型擦出就是把容器内对象的类型擦掉了,所谓的类型擦出,是指容器类 Bucket《Apple》,对于Apple的类型声明在编译期的类型检查之后被擦掉,变为和 Bucket《Object> 等同效果,也可以说是 Bucket《Apple> 和 Bucket《Banana> 被擦为和 Bucket《Object>等价,而不是指里面的对象本身的类型被擦掉!
泛型的补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者在强制转换了。
注意: 1:Java泛型是不支持基本类型。
2:在泛型类或者说泛型方法内部,我们无法获得任何类型信息,所以泛型不能用于显示的引用运行时类型的操作之中,例如转型、instanceof操作和new表达式。
为什么要使用泛型擦除?
这是一个历史问题,Java在JDK1.5以后出现的新特性,这就导致了很大一批原有类库是在不支持泛型的Java版本上创建的。而到后来Java逐渐加入了泛型,为了使得原有的非泛化类库能够在泛化的客户端使用,Java开发者使用了擦除进行了折中。