黑马程序员---泛型

   ------- android培训java培训、期待与您交流! ----------

入门泛型的基本应用:

泛型是jdk1.5的新特性。

没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。

package cn.itcast.day2;

import java.lang.reflect.Constructor;
import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) throws Exception {
		//不加泛型容易导致 类型转换出错!
		ArrayList collection1 = new ArrayList();
		collection1.add(1);
		collection1.add(1L);
		collection1.add("abc");
		//int x = (Integer)collection1.get(1);//这个集合的1角标位置是Long型的1L,转换为Integer时类型转换出错!
		//System.out.println(x);
		
		//加泛型后,只能添加<>里面指定的类型的元素
		ArrayList<String> collection2 = new ArrayList<String>();
		collection2.add("abc");
		String str = collection2.get(0);//获取元素的时候,不需要类型强制转换。
		System.out.println(str);//abc
		
		//new String(new StringBuffer("hello"));要用反射来实现这句话
		//先得到String(StringBuffer buffer) 构造函数
		//加泛型前:
		Constructor strConstructor1 = String.class.getConstructor(StringBuffer.class);
		String str2 = (String)strConstructor1.newInstance(new StringBuffer("hello"));//需要类型转换
		System.out.println(str2.charAt(1));//e
		
		//加泛型后:
		Constructor<String> strConstructor2 = String.class.getConstructor(StringBuffer.class);
		String str3 = strConstructor2.newInstance(new StringBuffer("hello"));//不需要类型转换
		System.out.println(str3.charAt(1));//e		
	}
}

Constructor is a raw type. References to generic type Constructor<T> should be parameterized.  构造函数是一个原始类型。泛型类型的构造函数的引用< T >应该是参数化的。

泛型是提供给 javac 编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型, getClass() 方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其 add 方法即可。

 

泛型的内部原理及更深应用:

泛型是给编译器看的。编译完成后,编译器会把泛型给去掉。这时 ArrayList<Integer> 和 ArrayList<String> 两个对象的类型是一致的,也可以说是同一份字节码。

泛型是给编译器看的,为什么通过反射可以越过它?
我直接给collection3里添加字符串,编译器是会报错的。透过编译器,进行反射可以通过。

package cn.itcast.day2;

import java.lang.reflect.Constructor;
import java.util.ArrayList;

public class GenericTest {

	public static void main(String[] args) throws Exception {
		//不加泛型容易导致 类型转换出错!
		ArrayList collection1 = new ArrayList();
		collection1.add(1);
		collection1.add(1L);
		collection1.add("abc");
		//int x = (Integer)collection1.get(1);//这个集合的1角标位置是Long型的1L,转换为Integer时类型转换出错!
		//System.out.println(x);
		
		//加泛型后,只能添加<>里面指定的类型的元素
		ArrayList<String> collection2 = new ArrayList<String>();
		collection2.add("abc");
		String str = collection2.get(0);//获取元素的时候,不需要类型强制转换。
		System.out.println(str);//abc
		
		//new String(new StringBuffer("hello"));要用反射来实现这句话
		//先得到String(StringBuffer buffer) 构造函数
		//加泛型前:
		Constructor strConstructor1 = String.class.getConstructor(StringBuffer.class);
		String str2 = (String)strConstructor1.newInstance(new StringBuffer("hello"));//需要类型转换
		System.out.println(str2.charAt(1));//e
		
		//加泛型后:
		Constructor<String> strConstructor2 = String.class.getConstructor(StringBuffer.class);
		String str3 = strConstructor2.newInstance(new StringBuffer("hello"));//不需要类型转换
		System.out.println(str3.charAt(1));//e	
		
		//编译完成后,编译器会把泛型给去掉。这时两个ArrayList对象的类型是一致的,也可以说是同一份字节码。
		ArrayList<Integer> collection3 = new ArrayList<Integer>();
		System.out.println(collection3.getClass()==collection2.getClass());//true
		
		//泛型是给编译器看的,为什么通过反射可以越过它?
		//我直接给collection3里添加字符串,编译器是会报错的:
		//collection3.add("abc");
		//透过编译器,进行反射:
		collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");//这个方法属于类的字节码身上的,与对象无关。得到这个方法以后,我再拿着这个方法去作用于某个对象。
		System.out.println(collection3.get(0));//abc。这时编译器提醒我打印出来的是Integer,可是运行的时候就不会去检查,你装进去什么,就把什么打出来。		
	}
}


ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:

Ø整个称为 ArrayList<E> 泛型类型
ØArrayList<E> 中的E称为类型变量或类型参数
Ø整个 ArrayList<Integer> 称为参数化的类型
ØArrayList<Integer> 中的 Integer 称为类型参数的实例或实际类型参数
ØArrayList<Integer> 中的 <> 念着 typeof
ØArrayList 称为原始类型
 
参数化类型与原始类型的兼容性:
Ø 参数化类型可以引用一个原始类型的对象,编译报告警告,例如,
Collection<String> c = new Vector(); //可不可以,不就是编译器一句话的事吗?
Ø 原始类型可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vector<String>(); //原来的方法接受一个集合参数,新的类型也要能传进去
 
参数化类型不考虑类型参数的继承关系:
ØVector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
ØVector<Object> v = new Vector<String>(); //也错误!
 
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:

   Vector<Integer> vectorList[] = new Vector<Integer>[10];

 
思考题:下面的代码会报错误吗?

Vector v1 = new Vector<String>();

Vector<Object> v = v1;

都不会报错的啦!

泛型的通配符扩展应用:

泛型中的?通配符

<?> 代表任意类型。

问题:
Ø定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
 
错误方式:

public static void printCollection(Collection<Object> cols) {

  for(Object obj:cols) {

  System.out.println(obj);

  }

  /* cols.add("string");//没错

   cols = new HashSet<Date>();//会报告错误!*/

}

 
正确方式:

public static void printCollection(Collection<?> cols) {

  for(Object obj:cols) {

  System.out.println(obj);

  }

  //cols.add("string");//错误,因为它不知自己未来匹配就一定是String

  cols.size();//没错,此方法与类型参数没有关系

   cols = new HashSet<Date>();

  }

 
总结:
Ø使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

 

泛型中的?通配符的扩展:

限定通配符的上边界:
Ø正确: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>();
 
提示:
Ø限定通配符总是包括自己。
Ø?只能用作引用,不能用它去给其他变量赋值

  Vector<? extends Number> y =new Vector<Integer>();

  Vector<Number> x = y;

  上面的代码错误,原理与Vector<Object > x11 =new Vector<String>();相似,

  只能通过强制类型转换方式来赋值。

泛型集合的综合应用案例:

能写出下面的代码即代表掌握了Java的泛型集合类:

 HashMap<String,Integer>hm = newHashMap<String,Integer>();

  hm.put("zxx",19);

  hm.put("lis",18);

 

  Set<Map.Entry<String,Integer>>mes=hm.entrySet();

  for(Map.Entry<String,Integer> me :mes) {

   System.out.println(me.getKey() + ":" +me.getValue());

  }

package cn.itcast.day2;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class GenericTest {

	public static void main(String[] args) throws Exception {
		//不加泛型容易导致 类型转换出错!
		ArrayList collection1 = new ArrayList();
		collection1.add(1);
		collection1.add(1L);
		collection1.add("abc");
		//int x = (Integer)collection1.get(1);//这个集合的1角标位置是Long型的1L,转换为Integer时类型转换出错!
		//System.out.println(x);
		
		//加泛型后,只能添加<>里面指定的类型的元素
		ArrayList<String> collection2 = new ArrayList<String>();
		collection2.add("abc");
		String str = collection2.get(0);//获取元素的时候,不需要类型强制转换。
		System.out.println(str);//abc
		
		//new String(new StringBuffer("hello"));要用反射来实现这句话
		//先得到String(StringBuffer buffer) 构造函数
		//加泛型前:
		Constructor strConstructor1 = String.class.getConstructor(StringBuffer.class);
		String str2 = (String)strConstructor1.newInstance(new StringBuffer("hello"));//需要类型转换
		System.out.println(str2.charAt(1));//e
		
		//加泛型后:
		Constructor<String> strConstructor2 = String.class.getConstructor(StringBuffer.class);
		String str3 = strConstructor2.newInstance(new StringBuffer("hello"));//不需要类型转换
		System.out.println(str3.charAt(1));//e	
		
		//编译完成后,编译器会把泛型给去掉。这时两个ArrayList对象的类型是一致的,也可以说是同一份字节码。
		ArrayList<Integer> collection3 = new ArrayList<Integer>();
		System.out.println(collection3.getClass()==collection2.getClass());//true
		
		//泛型是给编译器看的,为什么通过反射可以越过它?
		//我直接给collection3里添加字符串,编译器是会报错的:
		//collection3.add("abc");
		//透过编译器,进行反射:
		collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");//这个方法属于类的字节码身上的,与对象无关。得到这个方法以后,我再拿着这个方法去作用于某个对象。
		System.out.println(collection3.get(0));//abc。这时编译器提醒我打印出来的是Integer,可是运行的时候就不会去检查,你装进去什么,就把什么打出来。		
		
		printCollection(collection3);
		
		//泛型集合类:
		HashMap<String, Integer> hm = new HashMap<String, Integer>();
		hm.put("zxx", 28);
		hm.put("lhm", 35);
		hm.put("flx", 33);
		
		Set< Map.Entry<String,Integer> > entrySet = hm.entrySet();
		for(Map.Entry<String,Integer> entry : entrySet)
		{
			System.out.println( entry.getKey() + ":" + entry.getValue() );
		}		
	}
	
	private static void printCollection(Collection<?> coll) {
		for(Object obj : coll)
		{
			System.out.println(obj);
		}
		//coll.add("abc");//错误。因为它不知未来匹配就一定是String。
		coll.size();
		coll = new HashSet<Date>();//HashSet是Collection的子类。Date是一个表示时间的类。		
	}
}


Entry是Map的内部类:

对在jsp页面中也经常要对Set或Map集合进行迭代:

<c:forEach items=“${map}” var=“entry”>

  ${entry.key}:${entry.value}

</c:forEach>

自定义泛型方法及其应用:

C++的模板函数引入自定义泛型

 
如下函数的结构很相似,仅类型不同:
Øint add(int x,int y) {

  return x+y;

   } 

Øfloat add(float x,float y) {

  return x+y; 

 

Ødouble add(double x,double y) {

  return x+y; 

 

 
C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,示意代码如下:

  template<class T>

  T add(T x,T y) {

  return (T) (x+y);

  }

 

Java中的泛型类型(或者泛型)类似于C++中的模板。但是这种相似性仅限于表面,Java语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为Java厂商升级其JVM造成难以逾越的障碍。 

package cn.itcast.day2;

public class GenericTest {

	public static void main(String[] args) {
		
		//类型的推断:取最大公约数、或者取交集。
		Integer in = add(3 , 5);
		Number x1 = add(3.5 , 3);
		Object obj = add(3 , "abc");
	}
	
	//要定义一个类型必须在返回值之前
	private static <T>T add(T x, T y) {
		//return x + y; //The operator + is undefined for the argument type(s) T, T
		return null;
	}
	
}


泛型的实际类型只能是对象那种类型,不能是基本数据类型:

package cn.itcast.day2;

public class GenericTest {

	public static void main(String[] args) {
				
		//类型的推断:取最大公约数、或者取交集。
		Integer in = add(3 , 5);
		Number x1 = add(3.5 , 3);
		Object obj = add(3 , "abc");
		
		swap(new String[]{"abc","abc1","abc2"}, 1, 2);
		//swap(new int[]{1,2,3,4,5}, 3, 5);//报错。泛型的实际类型只能是对象那种类型,不能是基本数据类型。
	}
	
	//交换数组中的两个元素的位置的泛型方法
	public static <T>void swap(T[] arr, int x, int y) {
		T temp = arr[x];
		arr[x] = arr[y];
		arr[y] = temp;
	}
	
	//要定义一个类型必须在返回值之前
	private static <T>T add(T x, T y) {
		//return x + y; //The operator + is undefined for the argument type(s) T, T
		return null;
	}
	
}

只有引用类型才能作为泛型方法的实际参数,

对于add方法,使用基本类型的数据进行测试没有问题,这是因为自动装箱和拆箱了。

swap(new int[3],3.5);语句会报告编译错误,这是因为编译器不会对new int[3]中的int自动拆箱和装箱了,因为new int[3]本身已经是对象了,你想要的有可能就是int数组呢?它装箱岂不弄巧成拙了。

 

除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,

例如,Class.getAnnotation()方法的定义。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass)

并且可以用 & 来指定多个边界,如<V extends Serializable & cloneable> void method(){}

 

普通方法、构造方法和静态方法中都可以使用泛型。

编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:

   Vector<Integer> vectorList[] = new Vector<Integer>[10];

 

也可以用泛型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。

用下面的代码说明对异常如何采用泛型:

private static <Textends Exception> sayHello()throws T {

    try{

    }catch(Exception e){

        throw (T)e;

     }

}

 
在泛型中可以同时有多个泛型参数,在定义它们的尖括号中用逗号分隔,例如:

public static <K,V> V getValue(K key)  { return map.get(key); }

 

泛型方法的练习题

编写一个泛型方法,自动将Object类型的对象转换成其他类型。

package cn.itcast.day2;

public class GenericTest {

	public static void main(String[] args) throws Exception {				
		Object obj123 = "abc";
		String convert = autoConvert(obj123);		
	}
			
	//编写一个泛型方法,自动将Object类型的对象转换成其他类型。
	private static <T>T autoConvert(Object obj) {
		return (T)obj;
	}	
}

定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。

package cn.itcast.day2;

public class GenericTest {

	public static void main(String[] args) throws Exception {		
		String[] arr = new String[3];
		fillArray(arr,"abc");
	}
	
	//定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。
	private static <T>void fillArray(T[] arr, T obj) {
		for(int x=0; x<arr.length; x++)
		{
			arr[x] = obj;		
		}
	}		
}

采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。
package cn.itcast.day2;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

public class GenericTest {

	public static void main(String[] args) throws Exception {	
		ArrayList<Integer> collection3 = new ArrayList<Integer>();
		
		//透过编译器,进行反射:
		collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
		System.out.println(collection3.get(0));		
		
		printCollection(collection3);		
	}
	
	//采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。
	public static <T>void printCollection(Collection<T> coll, T obj1) {
		for(T obj : coll)
		{
			System.out.println(obj);
		}
		coll.add(obj1);
	}
		
	private static void printCollection(Collection<?> coll) {
		for(Object obj : coll)
		{
			System.out.println(obj);
		}
		//coll.add("abc");//错误。因为它不知未来匹配就一定是String。
		coll.size();
		coll = new HashSet<Date>();//HashSet是Collection的子类。Date是一个表示时间的类。		
	}
}
Ø 在这种情况下,前面的通配符方案要比范型方法更有效,当一个类型变量用来表达两个参数之间或者参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要使用范型方法。
 
定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。
package cn.itcast.day2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

public class GenericTest {
	public static void main(String[] args) throws Exception {		
		ArrayList<Integer> al = new ArrayList<Integer>();
		al.add(168);
		al.add(18);
		Integer[] intArr = new Integer[al.size()];
		copy(al, intArr);
		Arrays.sort(intArr);
		for(Integer inte : intArr)
		{
			System.out.println(inte);
		}
	}
	
	//定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。
	private static <T>void copy(Collection<T> coll, T[] arr) {
		Iterator<T> it = coll.iterator();
		for(int x=0; it.hasNext(); x++)
		{
			arr[x] = it.next();
		}
	}	
}
定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。
package cn.itcast.day2;

import java.util.Collection;
import java.util.Date;
import java.util.Vector;

public class GenericTest {

	public static void main(String[] args) throws Exception {		
		copy(new Vector<String>(), new String[10]);
		copy1(new Date[10], new String[10]);//取两个的最小公倍数,都被认为是Object[]。
		//copy(new Vector<Date>(), new String[10]);//报错,泛型的类型推断是由Vector的泛型传进来的。类型推断具有传播性。
	}
	
	//定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。
	private static <T>void copy1(T[] dest, T[] src) {
		for(int x=0; x<src.length; x++)
		{
			dest[x] = src[x];
		}
	}
	
	//定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。
	private static <T>void copy(Collection<T> coll, T[] arr) {
		Iterator<T> it = coll.iterator();
		for(int x=0; it.hasNext(); x++)
		{
			arr[x] = it.next();
		}
	}	
}


类型参数的类型推断

编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
 
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
Ø 当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:

   swap(new String[3],3,4)   à    static <E> void swap(E[] a, int i, int j)

Ø 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:

   add(3,5)   à static <T> T add(T a, T b)

Ø 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:

   fill(new Integer[3],3.5f)   à static <T> void fill(T[] a, T v)

Ø 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:

   int x =(3,3.5f)   à static <T> T add(T a, T b)

Ø 参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:

  copy(new Integer[5],new String[5]) àstatic <T> void copy(T[] a,T[]  b);

  copy(new Vector<String>(), new Integer[5]) àstatic <T> void copy(Collection<T> a , T[] b);

自定义泛型类的应用: 

如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:

  public class GenericDao<T> {

  private T field1;

  public void save(T obj){}

  public T getById(int id){}

  }

 
类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:
Ø GenericDao<String> dao = null;
Ø new genericDao<String>();
 
注意:
Ø 在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
Ø 当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
 
问题:类中只有一个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛型?(老师说如果类中的多个方法要使用泛型,使用类级别的)
package cn.itcast.day2;

import java.util.Set;

//Dao: Data Access Object(数据访问对象)--->crud(增删改查)
//为了制约这个类的方法操作的都是同一种对象,我们把泛型定义到了类上。
//我们不希望存进去的是猪,取出来的是狗。
public class GenericDao<T> {
	
	public void add(T p) {
		
	}
	
	public T findByID(int id) {
		return null;
	}
	
	public void delete(T obj) {
		
	}
	
	public void delete(int id) {
		
	}
	
	public void update(T obj) {
		
	}
	
	//类里面的静态方法是不可以用类上面定义的泛型的,类上的泛型是约束操作的对象的,
	//静态方法你连对象都不用就可以直接调用,所以还不知道你操作的是什么对象了。
	//public static void update2(T obj) {}  //错误,静态方法不能使用类上定义的泛型。
	
	//可以先提示您输入的用户名存不存在,再提示您输入的密码正不正确。
	public T findByUserName(String userName) {
		return null;
	}
	
	//可以提示您输入的用户名或密码错误!
	public T findByUserName(String userName, String password) {
		return null;
	}
	
	public Set<T> findByConditions(String where) {
		return null;
	}
}

package cn.itcast.day2;

import cn.itcast.day1.ReflectPoint;

public class GenericTest {

	public static void main(String[] args) throws Exception {		
		GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
		dao.add(new ReflectPoint(3,3));		
	}
	
}

package cn.itcast.day1;

import java.util.Date;

public class ReflectPoint {
	private Date birthday = new Date();
	
	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	private int x;
	public int y;
	private String str1;
	public String str2;
	public String str3;
	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	public ReflectPoint(int x, int y, String str1, String str2, String str3) {
		super();
		this.x = x;
		this.y = y;
		this.str1 = str1;
		this.str2 = str2;
		this.str3 = str3;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public String toString() {
		return x + "::" + y + "::" + str1 + "::" + str2 + "::" + str3;
	}
}

通过反射获得泛型的参数化类型

示例代码:

Class GenericalReflection {

          private Vector<Date> dates = new Vector<Date>();

          public void setDates(Vector<Date> dates) {

                   this.dates = dates;

          }

          public static void main(String[] args) {

                  Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric", Vector.class);

                  ParameterizedType pType = (ParameterizedType) (methodApply .getGenericParameterTypes())[0];

                  System.out.println("setDates("

                          + ((Class) pType.getRawType()).getName() + "<"

                          + ((Class) (pType.getActualTypeArguments()[0])).getName()

                          + ">)" );

          }

}

 
泛型DAO的应用:
Ø public abstract class DaoBaseImpl<T> implements DaoBase<T> {

            protected Class<T> clazz;

            public DaoBaseImpl() {

                      Type type = this.getClass().getGenericSuperclass();

                      ParameterizedType pt = (ParameterizedType) type;

                      this.clazz = (Class) pt.getActualTypeArguments()[0];

                      System.out.println("clazz = " + this.clazz);

            }

}

Ø public class ArticleDaoImpl extends DaoBaseImpl<Article> implements ArticleDao {

  }

 

Method类中的方法:

 Type[]getGenericParameterTypes()
          按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的。

Type类的子类:

 java.lang.reflect  接口 ParameterizedType

 表示参数化类型。

方法摘要
 Type[]getActualTypeArguments()
          返回表示此类型实际类型参数的 Type 对象的数组。
 TypegetOwnerType()
          返回 Type 对象,表示此类型是其成员之一的类型。
 TypegetRawType()
          返回 Type 对象,表示声明此类型的类或接口。
 

package cn.itcast.day2;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;

import cn.itcast.day1.ReflectPoint;

public class GenericTest {

	public static void main(String[] args) throws Exception {
				
		//我们用反射来得到这个集合中存放的对象的类型。
		//以后学webService的时候,有个方法返回一个集合,如果不知道这个集合里面元素的类型,那么就不知道要把那么数据转化成什么对象。
		//Vector<Date> v1 = new Vector<Date>();
		
		Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);//反射里方法的参数是没有泛型的。
		
		Type[] types = applyMethod.getGenericParameterTypes();
		
		/*Type下面有各种子类:Class是其中一个,
		  所有已知子接口: GenericArrayType, ParameterizedType(参数化类型), TypeVariable<D>, WildcardType 
		  所有已知实现类: Class */
		
		ParameterizedType pType = (ParameterizedType)types[0];//types[0]是applyMethod方法中的第一个参数。
		
		System.out.println( pType.getRawType() );//class java.util.Vector
		
		System.out.println( pType.getActualTypeArguments()[0] );//class java.util.Date		
	}
	
	//我没法通过v1来知道前面这个变量的类型,但是我可以通过这个方法来知道它的参数列表的类型。
	//因为这个Method类提供了相应的方法: 
	//Type getGenericReturnType()、获得这个方法的返回值类型
	//Type[] getGenericParameterTypes()、 获得这个方法的 以泛型那种类型的参数列表。
	//Class<?>[] getParameterTypes()、获得这个方法的参数列表
	public static void applyVector(Vector<Date> v1) {
		
	}
	
	//因为你在这里写一个参数:泛型是Integer的Vector,就已经报错了,编译器认为他们是同一个方法。
	/*public static void applyVector(Vector<Integer> v1) {
		
	}*/
	
}


 

 ------- Windows Phone 7手机开发.Net培训、期待与您交流! -------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值