1、说明
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序的非法输入,将报错的几率从运行期提前到了编译期,这使我们更方便的找到程序的错误,使我们的程序更安全。
·类型参数化与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译器不会报告错误,如:
Collection<String> c = new Vector();
原始类型可以引用一个参数化类型的对象,编译器不会报告错误,如:
Collection c1 = new Vector<String>();
·参数化类型不考虑类型参数的继承关系,编译器会报告错误,如:
Vector<String> v = new Vector<Object>();
Vector<Object> v1 = new Vector<String>();
·在创建数组实例时,数组的元素不能使用参数化类型,编译器会报告错误,如:
Vector<Integer> v2[] = new Vector<Integer>[10];
2、泛型中的?通配符
由于参数化类型不考虑类型参数的继承关系,这时候我们可以使用?通配符,使用?通配符可以引用其他这种参数化类型,?通配符定义的变量主要用作引用。可以调用与参数化无关的方法,不能调用与参数化有关的方法。
示例:
public static void printCollection(Collection<?> collection) {
collection.add("abc"); //错误,因为你给集合添加的是一个String的值,而collection不一定是String
collection.size(); //调用的是与参数化无关的方法
}
3、泛型中的?通配符的扩展
·限定通配符的上边界:
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>(); //错误
·提示
限定通配符总是包括自己
·示例
HashMap<String, Integer> maps = new HashMap<String, Integer>();
maps.put("zc", 20);
maps.put("gbb", 30);
Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();
for(Entry<String, Integer> entry : entrySet) {
System.out.println(entry.getKey() + ":" + entry.getValue());;
}
4、定义泛型方法
示例:
public static <T> void swap(T a[],int i,int j) {
T temp = a[i];
a[i] = a[j];
a[j] = temp;
}
调用:
swap(new String[]{"abc","xyz","zc"}, 1, 2); //正确
swap(new int[]{1,2,3,4},2,3); //错误,只有引用类型才能作为泛型方法的实际参数
说明:
·用于放置泛型的类型参数的尖括号应出现在方法的其他修饰符之后和在方法的返回类型之前。通常类型参数用单个字母的大写字母表示。
·只有引用类型才能作为泛型方法的实际参数
·在定义泛型的时候可以使用extends限定符,并且可以使用&指定多个边界。
·普通方法、构造方法和静态方法中都可以使用泛型。
·也可以使用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,不能用于catch子句中。如:
private static <T extends Exception> sayHello() throws T {
try {
} catch(Exception e) { //不能用T
throw (T)e
}
}
·在泛型中可以同时有多个类型参数,用逗号分隔:如:
public <K,V> getValue(K key){...}
5、定义泛型类型
1、如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个 实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法如下:
public class GenericDao<E> {
public void add(E obj){};
public E getById(int id){return null;};
}
2、类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,如:
GenericDao<String> dao = null;
new GenericDao<String>();
3、注意
·在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
·当一个变量被声明为一个泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态 变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类 级别的类型参数。如:public static void add(E obj){}; //这样是错误的
6、通过反射获得泛型的实际类型参数
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;
public class GenericTest {
public static void main(String[] args) {
try {
Method method = GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types = method.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
//得到原始类型class java.util.Vector
System.out.println(pType.getRawType());
//得到实际参数类型class java.util.Date
System.out.println(pType.getActualTypeArguments()[0]);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void applyVector(Vector<Date> v) {
}
}
注意:这就是很多框架实现自动转换类型的原理。