黑马程序员-java高新技术学习之泛型 (一)

2014-11-24 11:33:11 · 作者: · 浏览: 16


一,泛型基础知识


jdk1.5之后出现的新特性


泛型是提供给javac编译器使用的,它可以限定集合中输入的类型,让编译器挡住源程序中的非法输入,编译器编译带类型的说明的集合时会去除掉"类型"信息,是程序运行效率不受影响。对应参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。例如用反射得到的集合,再调用add方法即可。


泛型是编译器看的,所以通过反射可以避过编译器,继续把不同的类型的值放进已经被泛型限定了的集合里面。


体验泛型


Jdk 1.5以前的集合类中存在什么问题
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
int i = (Integer) collection.get(1);//编译要强制类型转换且运行时出错!
Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据
ArrayList collection2 = new ArrayList();
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*///这两行代码编译时就报告了语法错误
int i2 = collection2.get(0);//不需要再进行类型转换


泛型中的术语:
ArrayList类定义和ArrayList类引用中涉及如下术语:
整个称为ArrayList泛型类型
ArrayList中的E称为类型变量或类型参数
整个ArrayList称为参数化的类型
ArrayList中的Integer称为类型参数的实例或实际类型参数
ArrayList中的<>念着typeof
ArrayList称为原始类型


参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如, Collection c = new Vector();
原始类型可以引用一个参数化类型的对象,编译报告警告,例如, Collection c = new Vector();//原来的方法接受一个集合参数,新的类型也要能传进去
参数化类型不考虑类型参数的继承关系:
Vector v = new Vector(); //错误!///不写没错,写了就是明知故犯
Vector v = new Vector(); //也错误!
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector vectorList[] = new Vector[10];


为什么在创建数组实例时,数组的元素不能使用参数化的类型?
编译器是严格按照步骤走的。它检测每一行代码的信息是否有误,但是不会用执行时候的答案来检测代码的正确性。


泛型限定:


泛型的通配符(?)应用:?表示任意类型。比如当要定义一个任意参数化类型的集合(Collection< > col),就用得到了通配符。


使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。比如集合中的add()方法,使用了?的集合就不能在调用这个方法。


public void printCollection(Collection< > col){}


也就是说Collection< > col = new ArrayList();编译器让这个表达式被编译通过。

问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection cols) {
for(Object obj:cols) {
System.out.println(obj);
}
/* cols.add("string");//没错
cols = new HashSet();//会报告错误!*/
}
正确方式:
public static void printCollection(Collection< > cols) {
for(Object obj:cols) {
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet();
}
总结:
使用 通配符可以引用其他各种参数化的类型, 通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。


泛型中的?通配符的扩展

?extends E:可以接收E类型或者E的子类。这是上限限定
?super E:可以接收E类型或者E的父类。这是下限限定


限定通配符的上边界:
正确:Vector< extends Number> x = new Vector();
错误:Vector< extends Number> x = new Vector();


限定通配符的下边界:
正确:Vector< super Integer> x = new Vector();
错误:Vector< super Integer> x = new Vector();


提示:
限定通配符总是包括自己。
只能用作引用,不能用它去给其他变量赋值
Vector< extends Number> y = new Vector();
Vector x = y;
上面的代码错误,原理与Vector x11 = new Vector();相似,
只能通过强制类型转换方式来赋值。


自定义泛型


KV都可以代替任意类型的值,但是在java中范型的实际类型必须是引用类型


void get(K k,V v)
{
}


Java中的范型不能像C++那么灵活


T add(T a,T b)
{
//return a+b ;//很多人以为java也想C++一样可以这样 ,但是不可以 。
return null;
}


这个返回的null也是有类型限制的,比如上面的ab分别是Integer和String那么就会取他们共同的基类Object做为返回值类型,其他的同理
例子:
Number x1=add(3.5,3);//Integer和float他们共同的基类为Number
Object x2=add("abc",3);//Integer和String他们共同的基类为Object


实现任意类型的数组的成员值的交换,注意在自定义范型中范型的实际类型只能是引用数据类型不能是基本数据类型


public static void swap(T[]a,int x,int y)
{
T tem =a[x] ;
a[x]=a[y] ;
a[y]=tem ;
}
上面这个方法如果 swap(new Integer[]{1,2,3,4,5},1,2); //这样就会自动交换下标12的值
但是这样调用就错了 swao(new int[]{1,2,3,5,6},2,3) ; //所以说Java的范型的实际类型 只能是引用数据类型


泛型练习
1,编写一个泛型方法,自动将Object类型的对象转换成其他类型


public static T autoConvert(Object obj) //因为返回值是 T标识任意类型 所哟可以 将返回结果赋值给任意类型对象
{
return (T)obj;
}


Object obj=="abc";


String str=aut