首先说下泛型的好处:1.类型检查,编译器在编译器进行类型检查。
2.(自动)类型强转:提供安全的向下转型,获取具体对象。
?和 T 的区别:
?:表示持有某种的特定对象。(多种可能)
T: 表示持有具体的特定对象。(使用时被替换实际类型)
对通配符做的一些测试:
import java.util.*;
public class TestUnbounded{
static List list1;
static List<?> list2;
static List<?extends Object> list3;
static List<? extends Number> list4;
static List<Object> list5;
//原生类型可以被任何引用类型泛型引用,除了无边界通配符和自身,其他的都会有一个不受检的警告
static void f1(List list){
list1=list;
list2=list;
list3=list;
list4=list;
list5=list;
}
//都会报一个不受检异常,包括自身,且范围小于它的需要强转
static void f2(List<?> list){
list1=list;
list2=list;
list3=list;
list4=(List<Number>)list;
list5=(List<Object>)list;
list5.add("xu");
list5.add(3);
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
//都会报一个不受检异常,包括自身,且范围小于它的需要强转,使用通配符无法调用具体泛型方法
(参数类型不为它本身),因为它是不确定类型的,需要强转成具体类型调用,例如add(E e)
static void f3(List<? extends Object> list){
list1=list;
list2=list;
list3=list;
list4=(List<Integer>)list;
list5=(List<Object>)list;
list5.add("xu");
list5.add(2);
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//list3.add("xu2");对于add(String), 找不到合适的方法
}
static void f4(List<String> list){
list1=list;
list2=list;
list3=list;
list1.add(3);
System.out.println(list1.get(0));
//list4=(List<Number>)list;不能强转
//list5=(List<Object>)list;
}
public static void main(String []args){
f1(new ArrayList());
System.out.println("======");
f2(new ArrayList<>());
System.out.println("======");
f2(new ArrayList<String>());
System.out.println("======");
f3(new ArrayList<String>());
System.out.println("======");
//f3(new ArrayList<? extends Object>());错误: 意外的类型
f4(new ArrayList<String>());
System.out.println("======");
ArrayList alist=new ArrayList<String>();
alist.add(4);
System.out.println(alist.get(0));
System.out.println("======");
ArrayList<?> alist2=new ArrayList<String>();
ArrayList<Integer> alist3=(ArrayList<Integer>)alist2;
alist3.add(5);
System.out.println(alist3.get(0));
}
}
结果:
======
xu
3
======
xu
3
======
xu
2
======
3
======
4
======
5
总结:泛型实现是和引用类型泛型密切相关的,它表示你能往容器存放什么元素,当你想破坏容器使用泛型存储元素时,只需要用原生类型去引用容器。