黑马程序员-Java泛型
泛型
泛型是对Java语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。
没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,更方便。
示例:
public static void main(String[] args) throws Exception { Constructor<String> constructor = String.class.getConstructor(StringBuffer.class); String str = constructor.newInstance( new StringBuffer("abc" )); System. out.println(str.charAt(2)); //结果:c }
引入泛型后,创建实例对象时就不需要类型转换了。
泛型的内部原理
泛型时提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入。但是,编译器编译带类型说明的集合时会去掉“类型”信息,目的就是使程序运行效率不受影响。因此,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。
ArrayList<String> collection1 = new ArrayList<String>(); ArrayList collection2 = new ArrayList(); System. out.println(collection1.getClass() == collection2.getClass()); //结果:true
由于编译生成的字节码会取缔哦泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。
示例:用反射得到集合,再调用其add方法
public static void main(String[] args) throws Exception { ArrayList<Integer> collection1 = new ArrayList<Integer>(); collection1.getClass().getMethod( "add",Object.class).invoke(collection1, "abc"); System. out.println(collection1.get(0)); }
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如:Collection <String> c = new Vector();//考虑到对以前代码的兼容性,编译器是可以通过的
原始类型可以引用一个参数化类型的对象,编译报告警告,例如:Collection c = new Vector<String>();//原来的方法接收一个集合参数,新的类型也要能传进去
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!可以的话,那么以后从v中取出的对象当作String用,而v实际指向的对象中可以加入任意的类型对象
Vector<Object> v = new Vector<String>(); //错误!可以的话,那么以后可以向v中加入任意的类型对象,而v实际指向的集合中只能装String类型的对象
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型。
例如:Vector<Integer> vectorList[] = new Vector<Integer>[10]; //错误
泛型的通配符扩展应用
问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
如果将printCollection中的参数设置为Collection<Object>类型,那么传入参数的时候,如果参数化的类型是其他类型,就会报错。
public static void main(String[] args) { ArrayList<Integer> collection = new ArrayList<Integer>(); printCollection(collection); } //public static void printCollection(Collection<Object> collection){} public static void printCollection(Collection<?> collection){ //collection.add(1);//报错 System. out.println(collection.size()); for(Object obj : collection){ System. out.println(obj); } }
Java1.5中使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用。此时,printCollection方法中可以调用与参数化无关的方法,不能调用与参数化有关的方法。上面的示例Collection类中的add(E e)方法就与参数化有关,不能调用。size()方法与参数化无关就可以调用。
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
注意:限定通配符总是包括自己。