一、一个困惑
泛型我们经常会看到或者使用,这里首先记录一个自己曾经的困惑,下面的两个泛型方法有什么区别?
public void dosomething(T t){ } public <T> void dosomething(T t){ }
class Fruit { public String toString() { return "Fruit"; } } class Apple extends Fruit { public String toString() { return "Apple"; } } class Dog { public String toString() { return "Dog"; } } class Show<T> { void show_1(T t) { System.out.println("show_1 " + t.toString()); } <E> void show_2(E e) { System.out.println("show_2 " + e.toString()); } <T> void show_3(T t) { System.out.println("show_3 " + t.toString()); } public static void main(String[] args) { Show<Fruit> o = new Show<Fruit>(); Fruit f = new Fruit(); Apple a = new Apple(); Dog d = new Dog(); System.out.println("show_1 演示________________________"); o.show_1(f); o.show_1(a); /* * o.show_1(d); 把这行代码去掉注释,是不能编译通过的。因为在Show<T>中已经限定了全局的T为Fruit, * 所以此时的T只能是Fruit或者其子类,所以不能再加入Person; */ System.out.println("show_2 演示________________________"); o.show_2(f); o.show_2(a); /* * o.show_2(d); 正确。因为返回值void前面加了<E>,说明它与类中的<T>是可以不一样的,并不要求是Fruit或者其子类,可以是任何类型。 */ o.show_2(d); System.out.println("show_3 演示________________________"); o.show_3(f); o.show_3(a); /* * o.show_3(d); 正确。因为返回值void前面加了<T>,说明它与类中的<T>是可以不一样的,并不要求是Fruit或者其子类,可以是任何类型。 */ o.show_3(d); /*总结:只要类中限定了<T>(<E>,<R>等名字随便取,只要整个类中的一致),那么当初始化话该类的时候绑定的是什么,后面就各个方法中就只能传入相应的类型了, 除非在该方法前在写一个<T>(<E>,<R>等名字随便取,只要整个方法中的一致)表示该方法中可以传入的类型是随便的。*/ } }
<? extends T>和<? super T>
JAVA5.0的新的概念。由于它们的外表导致了很多人误解了它们的用途:
1.<? extends T>首先你很容易误解它为继承于T的所有类的集合,这是大错特错的,相信能看下去你一定见过或用过List<? extends T>吧?为什么我说理解成一个集合是错呢?如果理解成一个集合那为什么不用List<T>来表示?
所以<? extends T>不是一个集合,而是T的某一种子类的意思,记住是一种,单一的一种,问题来了,由于连哪一种都不确定,带来了不确定性,所以是不可能通过 add()来加入元素。你或许还觉得为什么add(T)不行?因为<? extends T>是T的某种子类,能放入子类的容器不一定放入超类,也就是没可能放入T。自己的理解是:由于传入的可以T类型的子类,但编译时编译器不知道运行时将会传进来的是什么,同时Java中不允许父类直接复制给子类的引用,就算强制类型转换了也不行(当然,如果A是B的父类,A a=new B(),此时是可以B B=(B)a是可以的),所以这里add什么都不能加了,除了可以代表一切类型的null。下面的代码说明了这一点。
package com.lxq.generics; import java.util.ArrayList; import java.util.List; class Fruit{ } class Apple extends Fruit{ } class RedApple extends Apple{ } public class TestGenerics { public static void main(String[] args){ TestGenerics tg=new TestGenerics(); tg.test1(new ArrayList<Fruit>()); tg.test2(new ArrayList<RedApple>()); } public void test1(List<? super Apple> apples){ /*既然传进来时apples是new ArrayList<Fruit>(),为什么不能添加 apples.add(new Fruit()); apples.add((Apple) new Fruit());*/ apples.add(new Apple()); apples.add(new RedApple()); } public void test2(List<? extends Apple> apples){ /*既然传进来时apples是new ArrayList<RedApple>(),为什么不能添加 apples.add(new Apple()); apples.add(new RedApple()); apples.add(new Object());*/ apples.add(null); } }
List<Number> l1=new ArrayList<Number>(); List<Integer> l2=new ArrayList<Integer>(); System.out.println(l1.getClass()); System.out.println(l2.getClass()); System.out.println(l1.getClass()==l2.getClass());编译器只为 ArrayList生成一个类。当生成了 ArrayList的字节码时,将很少剩下其类型参数的的跟踪。
三、一些示例
泛型上边界
flist1编译报错,不兼容的参数类型,集合认为虽然Apple继承自Fruit,因为泛型参数在声明时给定之后就被限制了,无法随着具体的初始化实例而动态改变,为解决这个问题,泛型引入了通配符”?”。声明List<? extends Fruit>,那么初始化的时候只要是Fruit及其子类即可,所以flist3和flist4就是正确的。
package com.lxq.generics; import java.util.ArrayList; import java.util.List; class Fruit{ } class Apple extends Fruit{ } class RedApple extends Apple{ } class Orange extends Fruit{ } public class TestGenerics { //Type mismatch: cannot convert from ArrayList<Apple> to List<Fruit> //List<Fruit> flist1 = new ArrayList<Apple>(); List<Fruit> flist2 = new ArrayList<Fruit>(); List<? extends Fruit> flist3 = new ArrayList<Apple>(); List<? extends Fruit> flist4 = new ArrayList<Fruit>(); }
通过<? super >限制了传入的List元素只能是Apple的父类,但是形参的类型实际上是List<Apple>的,所以apples.add((Apple) new Fruit());才一定要类型转换。如果一个类是泛型类也就是声明时是Classname<T>,那么泛型下边界还可以使用<?super T>,但是注意不能使用<Tsuper A>,即super之前的只能是泛型通配符,
package com.lxq.generics; import java.util.ArrayList; import java.util.List; class Fruit{ } class Apple extends Fruit{ } class RedApple extends Apple{ } class Orange extends Fruit{ } public class TestGenerics{ public static void main(String[] args){ TestGenerics tg=new TestGenerics(); tg.test(new ArrayList<Fruit>()); } public void test(List<? super Apple> apples){ //apples.add((Apple) new Fruit());运行时会报错Fruit cannot be cast to Apple apples.add(new Apple()); apples.add(new RedApple()); } }
泛型的通配符也可以不指定边界,没有边界的通配符意思是不确定参数的类型,编译时泛型檫除类型信息,认为是Object类型。如:
package com.lxq.generics; import java.util.ArrayList; import java.util.List; public class UnboundedWildcard{ static List list1; static List<?> list2; static List<? extends Object> list3; static void assign1(List list){ list1 = list; list2 = list; list3 = list; //有未检查转换警告 } static void assign2(List<?> list){ list1 = list; list2 = list; list3 = list; } static void assign3(List<? extends Object> list){ list1 = list; list2 = list; list3 = list; } public static void main(String[] args){ assign1(new ArrayList()); assign2(new ArrayList()); assign3(new ArrayList()); //有未检查转换警告 assign1(new ArrayList<String>()); assign2(new ArrayList<String>()); assign3(new ArrayList<String>()); List<?> wildList = new ArrayList(); assign1(wildList); assign2(wildList); assign3(wildList); } }List和List<?>的区别是:List是一个原始类型的List,它可以存放任何Object类型的对象,不需要编译时类型检查。List<?>等价于List<Object>,它不是一个原始类型的List,它存放一些特定类型,只是暂时还不确定是什么类型,需要编译时类型检查。因此List的效率要比List<?>高。
四、几个注意点
实现泛型接口注意事项
由于泛型在编译过程中檫除了参数类型信息,所以一个类不能实现以泛型参数区别的多个接口,类Hourly无法编译,因为由于泛型类型檫除,Payable<Employee>和Payable<Hourly>在编译时是同一个类型Payable,因此无法同时实现一个接口两次。
interface Payable<T>{ } class Employee implements Payable<Employee>{ } class Hourly extends Employee implements Payable<Hourly>{ }泛型方法重载注意事项
由于泛型在编译时将参数类型檫除,因此以参数类型来进行方法重载在泛型中要特别注意,无法通过编译,因为泛型檫除类型信息,上面两个方法的参数都被看作为Object类型,使用参数类型已经无法区别上面两个方法,因此无法重载。
public class GenericMethod<W,T>{ <span style="white-space:pre"> </span>void f(List<T> t) { <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>void f(List<W> v){ <span style="white-space:pre"> </span>} }
Java泛型-类型擦除http://blog.csdn.net/caihaijiang/article/details/6403349
Java 泛型http://www.cnblogs.com/friends-wf/p/3582841.html
简述泛型通配符<? extends T>和<? super T>http://hi.baidu.com/augustus_blog/item/d9331b3469b65a1d9dc65e69
《Java编程思想》学习笔记7——泛型编程基础http://blog.csdn.net/chjttony/article/details/6785221
《Java编程思想》学习笔记8——泛型编程高级http://blog.csdn.net/chjttony/article/details/6801406