泛型(Generics)
1、 泛型简介
(1)泛型,就是规定了集合、方法或者类接收的类型。规定好泛型,如果接收的类型不符合规定,源程序就会报错。
(2)泛型的好处。
如使用泛型集合,可将一个集合中的元素限定为一个特定的类型,集合中只能存储同一个类型的对象,这样更安全。当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
参数化的泛型类型,getClass方法的返回值和原始类型完全一样。比如ArrayList<String>和ArrayList<Integer>编译后得到的class文件是一样的。
利用反射,跳过编译器,就可以在集合中加入其它的类型。ArrayList<String>和ArrayList<Integer>都成为参数化的类型,ArrayList成为原始类型。
2、参数化类型与原始类型的兼容性。
(1)参数化类型可以引用一个原始类型的对象,编译报告警告,例如:Collection<String> c = new Vector();
(2)原始类型可以引用一个参数化类型的对象,编译报告警告,例如:
Collection c = new Vector<String>();
(3)参数化类型不考虑类型参数的继承关系,如:
Vector<String> v = new Vector<Object>();//错误
Vector<Object> v = new Vector<String>();//错误
(4)在创建数组实例时,数组的元素不能使用参数化的类型,例如:
Vector<Integer> vector[] = new Vector<Integer>[10]; //错误
3、泛型的通配符(?)扩展应用。
(1)通配符?,可以指向并接收任意类型,可以调用与参数化无关的方法,不能调用与参数化有关的方法,
如add方法,因为类型不确定。
(2)通配符扩展--限定上界和下界(包括自身):
<? extends T>可以接收T类型及其子类类型,当然,传入的是T及其子类,也都是可以接受T的。
<? super T>可以接收T类型及其父类型,当然传入的是T及其父类,也都是可以接受T的。
可以把一个具体的类型给?,不可以把?给一个具体类型。
4、泛型集合代码举例:
//定义一个HashMap集合,对其进行迭代,并将集合中key value值打印出来,使用泛型。
public static void genMapshow(){
HashMap<String,Integer> hm = new HashMap<String,Integer>();
hm.put("lihua",2);//定义一个key为String类型,Value为Integer类型的HashMap集合,并添加相应元素
hm.put("zhangxin",30);
hm.put("haiming",40);
Set<Map.Entry<String, Integer>> entrys = hm.entrySet();//通过相同泛型的Entry获得所有键值对。
for(Map.Entry<String, Integer> entry:entrys){//增强for循环,获取每一对键值并打印
System.out.println(entry.getKey()+"::"+entry.getValue());
}
}
5、自定义泛型方法及应用。
(1)自定义泛型方法简单一句话:在返回值前面加上<T>即可。例如:
public static <T> T add(T x,T y){//注意<T>类型声明要在函数返回值之前
return null;//无法返回x+y,因为不知道T类型是否可以进行+操作
}
(2)普通方法、构造方法和静态方法中都可以使用泛型。
(3)定义泛型时,也可以使用extends限定符,如public <A extends Annotation> A getAnnotation(Class<A> annotationCalss){}。并且可以 用&来指定多个边界,如<V extends Serializable&cloneable> void method(){}//这里可以至少一个是接口。
(4)也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中,
private static <T extends Exception> sayHello() throws T{
try{}catch(Exception e){throw (T)e;}
}
(5)在泛型中可以同时有多个类型参数,在定义他们的尖括号中用逗号分,例如:
public static <K,V> V getValue(K key){return map.get(key);}
(6)自定义泛型练习代码示例:
//采用自定义泛型方法的方式打印出任意参数化类型的集合中的所有内容。这种方法与前面讲到的通配符方法各有利弊。自己权衡。
private static <T> void printAny(Collection<T> con){
for(T t:con){
System.out.println(t);
}
}
//定义一个方法,把任意参数类型的集合中的数据安全的复制到相应类型的数组中。
private static <T> void copy1(Collection<T> con,T[] a){
for(int i = 0;i<a.length;i++){
con.add(a[i]);
}
}
//定义一个方法,把任意参数类型的一个数组中的数据安全的复制到相应类型的另一个数组中。
private static <T> void copy1(T[] a1,T[] a2){
for(int i = 0;i<a2.length;i++){
a1[i] = a2[i];
}
}
6、自定义泛型类及应用。
泛型类,在某个类上定义泛型:public class GenericDao<E>{}。
注意该自定义泛型类的静态方法不可以使用泛型类型,因为类的泛型只跟对象有关系,所以静态涉及不到。如果这个类中的静态方法想要使用泛型,可以单独定义泛型,那就是独立的泛型方法了。
代码示例:
public class GenericDao<E> {
public static void main(String[] args){}
public void add(E e){}//增
public E findById(int id){return null;}//按照ID查找
public E findByUserName(String name){return null;}//按照用户名查找对象
public void delete(E e){}//按照对象删除
public void delete(int id){}//按照id删除
public void update(E obj){}//更新,覆盖原来的对象
}