一、泛型Generic
理解泛型:
1、泛型是用来限定集合中的输入类型,是提供给javac编译器使用的,可以让编译器挡住源程序中的非法输入;
2、编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样;
3、由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,如下例所示。
//在集合中指定需要装入元素的类型
ArrayList<String> collection2 = new ArrayList<String>();
ArrayList<Integer> collection3 = new ArrayList<Integer>();
//去类型化,字节码中没有参数信息,true
System.out.println(collection3.getClass()==collection2.getClass());
//collection3.add("abc");透过泛型,添加字符
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
//即可以把String类型通过反射的方法加入Integer集合之中
泛型术语:如ArrayList<E>类定义和ArrayList<Integer>类的引用
1、整个称为ArrayList<E>泛型类型;
2、ArrayList<E>中的E称为类型变量或类型参数;
3、整个ArrayList<Integer>称为参数化的类型;
4、ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数;
5、ArrayList<Integer>中的<>念着typeof;
6、ArrayList称为原始类型。
参数化类型与原始类型的兼容性:
1、参数化类型可以引用一个原始类型的对象,编译器警告,例如,
Collection<String>c = new Vector();
2、原始类型可以引用一个参数化类型的对象,编译器警告,例如,
Collectionc = new Vector<String>();
参数化类型不考虑类型参数的继承关系:
Vector<String> v = newVector<Object>();错误!
Vector<Object> v = newVector<String>();错误!
编译器不允许创建泛型变量的数组:
Vector<Integer> vectorList[] = new Vector<Integer>[10];错误
泛型限定:
? extends E:可以接收E类型或E类型的子类,限定上限,如:
Vector<? extends Number> x = new Vector<Integer>();
? super E:可以接收E类型或E的父类型,限定下 限,如:
Vector<? super Integer> x = new Vector<Number>();
限定通配符总是包括自己,?只能用作引用,不能用它去给其他变量赋值。
二、泛型的实际应用
1、对集合中的元素进行泛型迭代;
//Map.Entry有key和value二个数据类型
HashMap<String,Integer> maps = new HashMap<String,Integer>();
maps.put("aaaa", 27);
maps.put("bbb", 26);
maps.put("cc", 23);
Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
for(Map.Entry<String,Integer> entry : entrySet){
System.out.println(entry.getKey()+"..."+entry.getValue());
}
2、定义泛型方法;
//定义一个方法,用于打印出任意参数化类型的集合中的所有数据
private static void printCollection(Collection<?> collection) {
/*Collection<?> 不能添加任何类型的元素,因为通配符不指定任何类型,
极有可能和传入类型相冲突;如果需要利用集合的相应方法,应该定义泛型方法
static <T> void printCollection(Collection<T> collection)*/
System.out.println(collection.size());
for(Object obj : collection){
System.out.println(obj);
}
}
//泛型方法定义时,需要放在返回值之前,按惯例,类型参数通常用单个大写字母表示
private static <T> void swap(T[] a, int i, int j) {
T temp = a[i];
a[j] = a[i];
temp = a[j];
}
//只有引用类型才能作为泛型方法的实际参数,8个基本类型不能
/*swap(new int[]{1,4},0,1);报错,因为编译器不会对new int[]中的int
自动拆箱和装箱了,因为new int[]本身已经是对象了,
你想要的有可能就是int数组呢?它装箱岂不弄巧成拙了。*/
swap(new String[]{"abc","baidu","qq"},1,2);
//可以通过,系统自动推断T为String类型
swap(new Integer[]{1,2,3,4},1,2);//通过,为Integer型
//泛型定义的返回值也为泛型
private static <T> T add(T i, T j) {
return T;
}
add(3,5);//自动装箱为Integer型
add(3,3.5);//Integer和float类型,系统自动推断为Number类型
add(3,"abc");
//Object x = add(3,"abc");整形和字符,自动推断为Object
//定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中
public static <T> void copy1(Collection<T> des,T[] s){
//迭代取出集合中的数据,再用赋值的形式增加入数组当中
int i=0;
for(T t : des){
s[i]=t;
System.out.println(s[i]);
i++;
}
}
//定义一个方法,把任意类型集合中的数据安全地复制到相应类型的数组中
public static <T> void copy2(T[] des,T[] src){
for(int i=0; i<src.length; i++){
des[i]=src[i];
System.out.println(des[i]);
}
}
//定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象
public static <T> void fillArray(T[] a,T obj){
for(int i=0; i<a.length; i++){
a[i] = obj;
}
}
3、定义泛型类型。
/*如果类的实例对象中的多处都要用到同一个泛型参数,
即这些地方引用的泛型类型要保持同一个实际类型时,
这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型*/
import java.util.Set;
class GenericDao<T> {
public void add(T obj){
}
public T findById(int id){
return null;
}
public void delete(T obj){
}
public void delete(int id){
}
public void update(T obj){
}
public Set<T> findByConditions(String where){
//得到泛型类型的集合Set
return null;
}
public T findByUserName(String name){
return null;
}
public static <E> void update2(E obj){
/*当一个变量被声明为泛型时,不能被静态变量和静态方法调用。
因为静态成员是被所有参数化的类所共享的,所以静态成员没有类级别的类型参数。
若需定义静态成员的泛型,需声明在方法上*/
}
}
class GenericDaoTest{
public static void main(String[] args){
//在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
GenericDao<String> dao = new GenericDao<String>();
dao.add("abcde");
}
}