疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java 泛型机制解析


 

 
一、泛型解析
Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用于集合里面。
不使用泛型:
Map m =newHashMap();
m.put("key","blarg");
String s =(String) m.get("key");
使用泛型:
Map<String,String> m =newHashMap<String,String>();
m.put("key","blarg");
String s = m.get("key");
以上二个例子已经说明的泛型的使用以及存在的意义。虽然只是多了一个类型转换,但这个操作可能导致类型转换异常ClassCastException,运行时异常往往让人难以检测到。保证列表中的元素为一个特定的数据类型,这样就可以取消类型转换,减少发生错误的机会,这也是泛型设计的初衷。
二、通配符
//不使用泛型
staticvoid print(Collection c){
Iterator iterator = c.iterator();
for(int i=0,len = c.size();i<len;i++){
System.out.println(iterator.next());
}
}
//使用泛型
staticvoid printTwo(Collection<Object> c){
for(Object object : c){
System.out.println(object);
}
}
//使用类通配符
staticvoid printThree(Collection<?> c){
for(Object object : c){
System.out.println(object);
}
}
 
 
以上三个方法的比较。在方法二中只能接受元素类型为Object类型的集合如ArrayList<Object>(),如果是ArrayList<String>,则会编译时出错。因为Collection<Object>并不是所有集合的超类。
方法三中使用了通配符?指定可以使用任何类型的集合作为参数。读取的元素使用了Object类型来表示,这是安全的,因为所有的类都是Object的子类。
常容易被忽略的问题:如果试图往使用通配符?的集合中加入对象,就会在编译时出现错误。需要注意的是,这里不管加入什么类型的对象都会出错。这是因为通配符?表示该集合存储的元素类型未知,可以是任何类型。往集合中加入元素需要是一个未知元素类型的子类型,正因为该集合存储的元素类型未知,所以我们没法向该集合中添加任何元素。唯一的例外是null,因为null是所有类型的子类型,所以尽管元素类型不知道,但是null一定是它的子类型。
List<?> a =newArrayList<String>();
a.add(null);
边界通配符:
如果你想从一个数据类型里获取数据,使用? extends通配符
如果你想把对象写入一个数据结构里,使用? super通配符
如果你既想存,又想取,那就别用通配符。
三、泛型方法
注意泛型方法的格式,类型参数<T>需要放在函数返回值之前。然后在参数和返回值中就可以使用泛型参数了。
publicstatic<T>void test(List<T> arrs){
//do something
}
四、常见问题
编译出错:类型擦除问题:List<String>和List<Integer>在运行事实上是相同的类型。他们都被擦除成他们的原生类型,即List。
publicvoid test(List<String> ls){
System.out.println("Sting");
}
publicvoid test(List<Integer> li){
System.out.println("Integer");
}
泛型和子泛型问题:
List<Apple> apples =newArrayList<Apple>();//1
List<Fruit> fruits = apples;//2
第1行代码显然是对的,但是第2行是否对呢?我们知道Fruit fruit = new Apple(),这样肯定是对的,即苹果肯定是水果,但是第2行在编译的时候会出错。这会让人比较纳闷的是一个苹果是水果,为什么一箱苹果就不是一箱水果了呢?可以这样考虑,我们假定第2行代码没有问题,那么我们可以使用语句fruits.add(new Strawberry())(Strawberry为Fruit的子类)在fruits中加入草莓了,但是这样的话,一个List中装入了各种不同类型的子类水果,这显然是不可以的,因为我们在取出List中的水果对象时,就分不清楚到底该转型为苹果还是草莓了。
 
通常来说,如果Foo是Bar的子类型,G是一种带泛型的类型,则G<Foo>不是G<Bar>的子类型。这也许是泛型学习里面最让人容易混淆的一点。