首先是泛型的使用,如下所示:
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>();
arr.add("abc");
//arr.add(1);
//arr.add(new Date());
}
在ArrayList集合后面加上<String>,这个集合的实例就只能装入String类型的对象了,所以加入int类型的和Date类型的对象都会报错。好处就是在使用集合中的元素时不用强制转换。
其实泛型的使用是针对编译器的,使用泛型只是告诉编译器要限定集合中的输入类型,挡住不允许的非法输入,但程序运行时就会把泛型的限定去掉,提高运行的效率,如:
public static void main(String[] args) throws Exception{
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.getClass().getMethod("add", Object.class).invoke(arr, "abc");
System.out.println(arr.get(0));
}
ArrayList中元素类型为Integer类型,但用反射的方法调用add方法,传入字符串"abc",没有报错,最后的运行结果为abc,证明程序在运行的时候是去掉了泛型信息的。
需要注意的是:参数话类型时不考虑参数的继承关系,如:
ArrayList<Object> arr = new ArrayList<String>;
因为不考虑继承关系,所以这句话会报错,就算String类型是Object的子类,也不能赋值给它。
然后是泛型中的通配符的使用:
1:通配符?可以引用其它参数的类型,如:
public static void main(String[] args) throws Exception{
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(1);
arr.getClass().getMethod("add", Object.class).invoke(arr, "abc");
test(arr);
}
public static void test(ArrayList<?> arr){
//arr.add(2);//错误
for(Object obj:arr){
System.out.println(obj);
}
}
在test方法参数中有个ArrayList<?>类型的参数,通配符?在这里的作用是说明ArrayList里面的元素可以是任何类型,所以无论外部传入是的ArrayList<Integer>还是ArrayList<String>还是没有参数化的ArrayList,test方法都能接受且正确运行。这里需要注意:因为用了通配符?,所以在test方法中不确定传入的参数的类型,自然不能使用这些和具体类型有关的方法,如add()方法,但与类型无关的方法可以使用。
2:然后是通配符的扩展,如:
ArrayList<? extends Number> x;
ArrayList<? super Integer> y;
在通配符后面跟上了extends或super,分别指定通配符的上边界和下边界,即:指定上边界时,集合的元素只能是上边界的类或子类的实例,不能为其它类的实例,如x中元素可以为Integer类型,可以为Long类型,但不能为String;指定下边界时,集合的元素只能是下边界的类或下边界类的父类的实例,如y中的元素可以为Integer类型,也能为Number类型,但不能为Long类型或Byte类型