1.深入泛型
当使用List类型时,如果为E形参传入String类型实参,则产生了一个新的类型:List<String>类型。可以为任何类、接口增加泛型声明。
从泛型类派生子类
使用接口、父类时不能再包含类型形参。例如下面的代码就时错误的:
public class A extends Apple<T>{}
可以改为:
public class A extends Apple<String>
不管泛型的实际类型参数是什么,它们在运行时总有同样的类,在内存中也只占用一块内存空间,如下所示:
List<String> l1 = new ArrayList<>(); List<Integer> l2 = new ArrayList<>(); System.out.println(l1.getClass() == l2.getClass());//输出true
在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参,同时由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类。
2.类型通配符
现有如下代码将会发出泛型警告:
public void test(List c) { for(int i = 0;i < c.size();i++) { System.out.println(c.get(i)); } }
如果改为如下形式:
public void test(List<Object> c) { for(int i = 0;i < c.size();i++) { System.out.println(c.get(i)); } }
调用该方法是将会引发错误,例如:
无法将Test中的test(java.util.List<java.lang.Object>) 应用于(java.util.List<java.lang.String>)
如果Foo是Bar的一个子类,而G是具有泛型声明的类或接口,G<Foo>并不是G<Bar>的子类型,而数组则不同,Foo[]依然是Bar[]的子类型
为了表示各种泛型的父类,可以使用类型通配符:
public void test(List<?> c) { for(int i = 0;i < c.size();i++) { System.out.println(c.get(i)); } }
现在便可以使用任何类型的List调用它,对于Set<?>、Collection<?>、Map<?,?>同理
但这种带通配符的List仅表示各种泛型List的父类,并不能把元素加入其中,例如,如下代码将会引起编译错误:
List<?> c = new ArrayList<String>(); c.add(new Object());
唯一的例外是null,因为它是所有引用类型的实例
设定类型通配符的上限
List<? extends Shape>可以表示List<Circle>、List<Rectangle>的父类——只要List后尖括号里的类型是Shape的子类型即可。可以把Shape称为这个通配符的上限。类似地,不能把Shape对象或其子类的对象加入这个泛型集合中。例如,以下代码就时错误的:
public void addRectangle(List<> extends Shape> shapes) { shapes.add(0,new Rectangle()); }
设定类型形参的上限
public chasse Apple<T extends Number>
在另一种更极端的情况下,程序需要为类型形参设定多个上限:
public class Apple<T extends Number & java.io.Serializable> { ... }
3.泛型方法
定义泛型方法
所谓泛型方法,就是在声明方法时定义一个或多个类型形参,语法格式如下:
修饰符 <T,S> 返回值类型 方法名(形参列表) { //方法体... }
例如:
static <T> void fromArrayToCollection(T[] a,Collection<T> c) { for(T o:a) { c.add(o); } }与类、接口中使用泛型参数不同的是,方法中的泛型参数无须显式传入实际类型参数