jdk1.5以后有了泛型的概念,那么究竟泛型有什么好处呢?
在没有泛型的时候,只要是对象,不管什么类型都可以存储进同一个集合中,使用泛型集合,可以将一个集合中
的元素限定为一个类型,泛型中只能存储同一个类型的对象,在获取其中的对象时,无需再进行类型强转的操作。
编译器在编译时会去掉泛型中指定的类型信息,利用反射的方式可以在一个<String>的泛型中加入一个Integer的对象。
泛型中的常用术语:
ArrayList<E> 称为泛型类型
E 称为类型变量或类型参数
ArrayList<Integer> 称为参数化的类型
其中Integer称为类型参数的实例或实际类型参数
尖括号<> 读作typeof
ArrayList 称为原始类型
参数化类型与原始类型的兼容性:
1,参数化类型可以引用一个原始类型的对象,编译报告警告,例如:
Collection<String> c = new Voctor();
2,原始类型可以引用一个参数化类型的对象,编译报告警告,例如:
Collection c = new Voctor<String>();
参数类型不考虑类型参数的继承关系,下面两个例子的写法都是错误的
Voctor<String> v = new Voctor<Object>();//错误的
Voctor<Object> v = new Voctor<String>();//错误的
在创建数组实例的时候,数组的元素不能使用参数化类型,例如下面
Voctor<Integer> v[] = new Voctor<Integer>[10];//错误的
可以将泛型定义到类上:
package com.east.firt;
import java.util.Set;
// dao : data access object--->crud(对数据库的增删改查操作)
public class GenericDao<T> { //将泛型定义到类上,类里面的多个方法用的是同一个类型
public void add(T x){
}
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){
return null;
}
}
泛型中?通配符
/*
?通配符的简单使用
*/
public static void printCollection(Collection<?> c)
{
for(Object obj:c)
{
System.out.println(obj);
}
//cols.add("String");//错误,因为不知道到未来匹配的一定就是String
c.size();//ok,此方法与类型参数无关
c = new HashSet<Date>();
}
总结:
1、“?”表示任何类型,使用”?"通配符可以引用其他各种参数化的类型。"?"通配符定义的变量主要用作引用,
可以调用与参数化无关的方法,不能调用与参数化有关的方法。
2、泛型中?通配符的扩展
限定通配符的上边界:
Vector<? extends Number> = new Vector<Ingeter>();
限定通配符的下边界:
Vector<? super Ingeter>
限定通配符总是包括自己
泛型集合的综合应用案例
package com.east.firt;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
public class GenericTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
ArrayList<String> collection2 = new ArrayList<String>();
collection2.add("abc");
String element = collection2.get(0);
// System.out.println(element);
// printCollection(collection2);
HashMap<String,Integer> maps = new HashMap<String,Integer>();
maps.put("tqf", 26);
maps.put("jyj", 25);
maps.put("wyl", 26);
Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();
for(Map.Entry<String, Integer> entry:entrySet){
System.out.println(entry.getKey()+":"+entry.getValue());
}
swap(new String[]{"abc","xyz","cast"},1,2);
//为一个类定义泛型
GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
dao.add(new ReflectPoint(3,3));
// String s = dao.findByid(1);
ReflectPoint rp = dao.findByid(1);
//用反射获得泛型的实际类型参数
Method applyMethod =
GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType ptype = (ParameterizedType)types[0];
System.out.println(ptype.getRawType());
System.out.println(ptype.getActualTypeArguments()[0]);
}
public static void applyVector(Vector<Date> v1){
}
//打印任意类型的集合中的元素,这里使用?通配符
public static void printCollection(Collection<?> 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 tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
//将任意类型的数组中的所有元素填充为相应类型的某个对象
private static <T> void fillArray(T[] a,T obj){
for(int i =0;i<a.length;i++){
a[i] = obj;
}
}
private static <T> T add(T x,T y){
return y;
}
}
从上面的综合应用案例中总结出关于泛型使用的以下几点:
1、泛型中的实际类型参数必须是引用类型,不可以是基本类型。
2、用于放置泛型的类型参数的尖括号应该出现在其他所有修饰符之后和在方法的返回类型之前,
也就是紧邻返回值之前,按照惯例,类型参数通常用大写字母表示。
3、只有引用类型的参数才能作为泛型方法的实际参数,例如swap(new int[]{1,2,3})会编译不通过。
4、除了应用泛型时可以使用extends限定符,在定义泛型时也可以使用。
5、普通方法、构造方法和静态方法都可以使用泛型。
6、也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但不能用于
catch子句中。
7、在泛型中可以同时拥有多个类型参数,在定义他们的尖括号中用逗号隔开,例如:
public static <K,V> V getValue(K key){
return map.get(key);
}
采用自定义泛型的方式打印出任意参数化类型的集合中的所有内容,这个方法的实现在上例中使用
了?通配符,因为这时通配符比泛型方法更有效,当一个类型变量用来表达两个参数之间或者参数
和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法代码中
也被使用而不仅在签名的时候使用,才需要使用泛型方法。