笔记摘要:
这里总结了JDK1.5中的新特性:泛型,介绍了泛型的作用、泛型方法、泛型类的定义、泛型接口、泛型的限定进行了介绍,最后说明了如何通过反射获取泛型的实际参数类型的方式
一、概述
1. 好处
1> 提高了程序的安全性
2> 将运行时期出现问题ClassCastException,转移到了编译时期。,
方便于程序员解决问题,让运行时问题减少,安全。
3> 避免了强制转换麻烦。
4> 泛型类的出现优化了程序设计
2、泛型格式:通过< >来定义要操作的引用数据类型。
3、什么时候使用泛型?
当类中要操作的“引用数据类型”(不能是基本数据类型)不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成扩展。
二、了解泛型
1、 ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
2、 参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如,
Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?原始类型可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去3、 参数化类型不考虑类型参数的继承关系:
Vector<String> v = newVector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
Vector<Object> v = newVector<String>(); //也错误!
4、 编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer> vectorList[] = newVector<Integer>[10];
三、泛型类
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,
也就是类级别的泛型,定义的泛型在整个类中有效。如果被方法使用,那么如果泛型类的对象已经明确操作的具体类型后,所有要操作的类型就已经固定了。
语法格式如下:
public classGenericDao<T> {
private T field1;
public void save(T obj){}
public T getById(int id){}
}
类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:
GenericDao<String> dao =null;
new genericDao<String>();
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
泛型类演示代码:
/*只能操作工人类型的工具类class Tool{ private Worker w; public void setWorker(Worker w) { this.w = w; } public Worker getWorker() { return w; }} */class Worker { //doSomething();}class Student3 { //doSomething();}// 泛型前做法,由于接收的是Object,所以需要强转class Tool { private Object obj; public void setObject(Object obj) { this.obj = obj; } public Object getObject() { return obj; }}// 泛型类:操作传入类型,提高了扩展性class Utils{ private T t; public void setObject(T t) { this.t = t; } public T getObject() { return t; }}class GenericDemo3 { public static void main(String[] args) { Utils u = new Utils (); u.setObject(new Worker()); Worker w = u.getObject();; /* Tool t = new Tool(); t.setObject(new Student()); Worker w = (Worker)t.getObject(); */ }}
三、泛型方法:
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
特殊: 静态方法的泛型
静态方法不可以访问类上定义的泛型:静态先加载,如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上
定义泛型方法
1、 Java的泛型方法没有C++模板函数功能强大,java中的如下代码无法通过编译:
<T> T add(T x,T y) {
return (T) (x+y);
//return null;
}
用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。
按照惯例,类型参数通常用单个大写字母表示。
2、 交换数组中的两个元素的位置的泛型方法语法定义如下:
static <E> void swap(E[] a, int i,int j) {
Et = a[i];
a[i]= a[j];
a[j]= t;
}
3、 只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5);语句会报告编译错误。
4、 除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。
并且可以用&来指定多个边界,如<Vextends Serializable & cloneable> void method(){}
5、 普通方法、构造方法和静态方法中都可以使用泛型。
6、 也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。
7、 在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:
public static <K,V> V getValue(K key){ return map.get(key);}
泛型方法与泛型类比较示例代码:
说明:泛型类和泛型方法没有绝对的界限,而且可以混合使用,如果要操作某一特定类型T的数据,
就可以定义为泛型类,所有方法也可以定义为T类型,就像集合中的那样。
而泛型方法更具灵活性,可以操作所传入的任意类型。
class Demo{ public void show(T t){ System.out.println("show:"+t); } public void print(T t){ System.out.println("show:"+t); } } *///可以在泛型类中定义泛型方法class Demo { // 操作的类型为 public void show(T t) { System.out.println("show:" + t); } // 操作的类型为传入的任意类型 public void print(Q q) { System.out.println("print:" + q); } // 静态方法不可以访问类上定义的泛型 public staticvoid method(W t) { System.out.println("method:" + t); }}class GenericDemo4 { public static void main(String[] args) { Demo d = new Demo (); d.show("haha"); // d.show(4); d.print(5); d.print("hehe"); Demo.method("hahahahha"); /* 泛型定义在类上,需在建立对象时就明确操作类型 Demo d = new Demo (); d.show(new Integer(4)); d.print("hah"); Demo d1 = new Demo (); d1.print("haha"); d1.show(5); */ }}
四、泛型接口
泛型接口示例代码:
//泛型定义在接口上。interface Inter{ void show(T t);}/*将接口实现定义为String,只能操作String类型数据class InterImpl implements Inter { public void show(String t){ System.out.println("show :"+t); }}*///将接口实现类定义为不确定的类型,操作传入的类型class InterImpl implements Inter { public void show(T t){ System.out.println("show :"+t); }}class GenericDemo5 { public static void main(String[] args) { InterImpl i = new InterImpl (); i.show(4); //InterImpl i = new InterImpl(); //i.show("haha"); }}
五、泛型高级应用:泛型中的?通配符的扩展
1、泛型的限定:
通配符“?”,也可以理解为占位符。
2、上限和下限
上限:? extends E : 可以接收E类型或者E的子类型
下限:? super E :可以接收E类型或者E的父类型
提示:
1、在使用下限的时候,一般在传入的为父类,因为这样,只要是子类就可以接收,提高了扩展性,但是只能用父类的方法。
2、 限定通配符总是包括自己。
3、?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = newVector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。
泛型高级应用示例一:
说明:泛型的下限限定
发现的问题:
在本例中由于父类已经有getName方法,但是初始化的时候却使用了super(name),所以使用的是
不同的name,在子类使用getName时,获取的为null。
import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;class Person4 { private String name; Person4(String name) { this.name = name; } public String getName() { return name; }}class Student4 extends Person4 { private String name; Student4(String name) { super(name); } // 这里定义之后打印的是null,因为覆盖了父类的getName方法,但是初始化是到父类初始化, // 所以使用的不是同一个name,本类中的name并没有赋值 public String getName() { return name; }}class GenericDemo6 { public static void main(String[] args) { /* ArrayListal = new ArrayList (); al.add("abc1"); al.add("abc2"); al.add("abc3"); printColl(al); ArrayList al1 = new ArrayList (); al1.add(4); al1.add(7); al1.add(1); printColl(al1); */ ArrayList al = new ArrayList (); al.add(new Person4("abc1")); al.add(new Person4("abc2")); al.add(new Person4("abc3")); printColl(al); ArrayList al1 = new ArrayList (); al1.add(new Student4("abc--1")); al1.add(new Student4("abc--2")); al1.add(new Student4("abc--3")); printColl(al1); // ArrayList al = new // ArrayList ();error } public static void printColl(Collection al)// 接收Person及Person的子类型,多态 { Iterator it = al.iterator(); while (it.hasNext()) { System.out.println(it.next().getName()); } } /* 如果明确定义为 类型,可以接收并操作T类型,而 因为没确定类型, 所以不可以使用类型的特有方法 public static void printColl(ArrayList al){ //ArrayList al = new ArrayList ();error Iterator it = al.iterator(); while(it.hasNext()) { System.out.println(it.next().toString()); } } */}
泛型高级应用示例二:
示例说明:通过compareble<T>和comparetor来说明泛型的上限限定
发现的问题:
在本例中由于父类已经有getName方法,但是初始化的时候却使用了super(name),所以使用的是不同的name,在子类使用getName时,获取的为null,而CompareTo方法比较的是两个对象,所以出现了NullPointerException
教训:
子类没有特有功能要体现的时候,不要去复写父类的方法,以免对不同的变量赋值,最终出现NullPointerException
import java.util.Comparator;import java.util.Iterator;import java.util.TreeSet;class Person5 { private String name; Person5(String name) { this.name = name; } public String getName() { return name; }}class Student5 extends Person5 implements Comparable// 接收E及E的父类型,这里的E指Student{ private String name; Student5(String name) { super(name); }//返回的是子类的name,而初始化的是父类的name;//切记:没有实现特别需求时不用覆盖 /*public String getName(){ Return name;}*/ public int compareTo(Person5 s) { return this.getName().compareTo(s.getName()); }}// 因为是继承关系,所以子类有父类的方法,如果类型定义为父类,// 那么使用的是父类方法,所以可以传入父类,一般也应该定义为父类//那么整个继承体系都可以使用该比较器,提高了扩展性class Comp implements Comparator // 接收E及E的父类型,这里的E指所传入的继承体系中的子类型{ public int compare(Person5 p1, Person5 p2) { // Person s1 = new Student("abc1"); return p1.getName().compareTo(p2.getName()); }}class GeneticDemo { public static void main(String[] args) { TreeSet ts = new TreeSet (new Comp()); ts.add(new Student5("abc1")); ts.add(new Student5("abc8")); ts.add(new Student5("abc5")); ts.add(new Student5("abc3")); Iterator it = ts.iterator(); while (it.hasNext()) { System.out.println(it.next().getName()); } }}
六、通过反射获得泛型的参数化类型
我们可以通过反射的方式得到使用泛型的方法之后,通过方法上getGenericParameterTypes()方法获取泛型的参数化类型
示例代码:
import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.util.Date;import java.util.Vector;class GenericalReflection { private Vectordates = new Vector (); public void setDates(Vector dates) { this.dates = dates; } public static void main(String[] args) throws NoSuchMethodException, SecurityException { //通过反射获取applyVector方法 Method methodApply = GenericalReflection.class.getDeclaredMethod("applyVector", Vector.class); //获取通过方法调用getGeneticParameterTypes()获取参数类型 ParameterizedType pType = (ParameterizedType) (methodApply .getGenericParameterTypes())[0]; //打印参数类型信息 System.out.println("setDates(" + ((Class) pType.getRawType()).getName() + "<" + ((Class) (pType.getActualTypeArguments()[0])).getName() + ">)" ); } //使用泛型的方法 public static void applyVector(Vector v1){}}
对于HashSet中的equals方法,由于继承自Object,所以没有泛型,必须强转。