参考

张孝祥系列

JDK 1.8 API

场景

为什么java后来要引入泛型?泛型原理与特性?如何自定义方法泛型与类泛型?如何通过反射获取参数化类型的实际类型参数

分析
<pre name="code" class="java">package cool.pengych.java.generic;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
/**
 * @author pengyucheng
 * 泛型总论
 * 泛型是提供给javac编译器使用的,可以限定集合或者其他类的输入类型,让编译器挡住源程序中的非法输入。
 * 
 * 一、泛型的初体验与泛型相关术语解说(以ArrayList为例)
 * ArrayList:原始类型
 *  ArrayList<E>: 泛型类型
 *  ArrayList<Integer>:参数化的类型
 *  ArrayList<Integer>中的Integer称为:类型参数的实例或实际类型参数
 *  
 * 二、内部原理
 *  
 *  编译器编译带类型说明的集合时会去掉“类型”信息,使程序效率不受影响,对于参数化的泛型类型,getClass()
 *  返回值(运行时信息)和原始类型完全一样。由于编译生成的字节码会去掉泛型信息,这样,只要能跳过
 *  编译器,就可以往某个范型集合中加入其他类型的数据。例如,用反射得到集合,再调用其add方法。
 *  
 * 三、泛型的特性
 * 
 * 四、自定义泛型
 *  1、在方法上定义泛型:返回值之前申明泛型  T
 *        举例 : ·[public,...] <T [extends Serializable & Cloneable ]> T add(T x, T y){ }
 *  2、在类上定义泛型:类名称后申明<T>
 *  
 *  五、泛型综合应用
 */
public class GenericTest
{
	public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException
	{
		// 问题演示:以下代码容易出现类型转换异常
		List collection = new ArrayList();
		collection.add("test");
		collection.add(2);
		//int i = (int) collection.get(0); // java.lang.ClassCastException
		 
		//泛型初体验:限定输入类型
		List<String> collection2 = new ArrayList<String>();
		collection2.add("test");
		//collection2.add(1);//编译时报错
		
		// 泛型原理:编译时检查范型类对象参数是否合法,编译后的class文件会去泛型化
		ArrayList<String> strs = new ArrayList<String>();
		strs.add("abc");
		ArrayList<Integer> ints = new ArrayList<Integer>();
		
		System.out.println(strs.getClass() == ints.getClass());// 结果true : 证明方式一
		ints.getClass().getMethod("add", Object.class).invoke(ints, "this is string");
		System.out.println(ints.get(0)); //  this is string :证明方式二 - 在Integer类集合ArrayList中通过反射加入 String类型元素
	
	   /*
	    * 一、泛型之参数化类型特性
	    * 1、与原始类型的完全兼容
	    * 2、不考虑类型参数的继承关系
	    */
		List list = new ArrayList<String>();
		ArrayList<String> lists = new ArrayList(); // 与原始类型完全兼容
		// Vector<Object> v = new Vector<String>() ;// 编译时异常:即使Object是 String的父类,Vector<Object>不是Vector<String>的父类
		
		printCollection(ints);
		add(3,5);
		Number num = add(3.9,5); // 取最大公约数
		Object obj = add(3,"abc");
		
		/*
		 * 综合应用
		 * 1、Map的打印
		 * 2、通过反射的方式获取 参数化类型的实际类型参数
		 */
		testGeneric();
		findPerson(new Vector<Date>());
	}
	/**
	 * 泛型特性之通配符
	 * 1、  ?  : 通配所有
	 * 2、 上、下界通配符(都包括 Integer本身) :<? extends Integer >、< ?   super Integer >
	 */
	public static void printCollection(Collection<?> collections)
	{
		// collections.add("ABC"); //编译时报错
		collections.size();
		List<? extends Number> lists = new ArrayList<Integer>();
		// List<? extends Number> lists2 = new ArrayList<String>();
	}
	/**
	 * 自定义泛型
	 */
	private static <T> void swap(T[] arr,int i ,int j)
	{
		T tmp = arr[i];arr[i] = arr[j]; arr[j] = tmp;
	}
	
	private static <T> T add(T x,T y)
	{
		return y ;
	}
	/**
	 *  泛型综合应用
	 *  1、Map的打印
	 *  2、通过反射的方式获取 参数化类型的实际类型参数
	 */
	private static void testGeneric()
	{
		Map<String, Integer> maps = new HashMap<String,Integer>();
		maps.put("ivy", 27);
		maps.put("nice", 27);
		
		Set<Map.Entry<String, Integer>> infos = maps.entrySet();
		for (Entry<String, Integer> entry : infos) 
		{
			System.out.println(entry.getKey()+":"+entry.getValue());
		}
	}
	/**
	 *  public Vector[Person] findById(int id){...}
	 *  
	 *  现象:根据findPerson方法的参数 Vector<Date>  中的Date  =>  该方法返回类型Set<T>中的T为 Date  - 这是如何实现的? 
	 *  没法通过 v1 直接 => Vector<Date>中的参数类型是 Date ,但是可以通过以接收Vector<Date> v1为参数的方法间接推断出Vector中的参数类型是Date
	 */
	public  static <T> Set<T> findPerson(Vector<Date> v1) throws NoSuchMethodException, SecurityException
	{
		applyVector( v1);
		Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
		Type[] types = applyMethod.getGenericParameterTypes();
		ParameterizedType pType =  (ParameterizedType) types[0];
		System.out.println(pType.getRawType());// class java.util.Vector
		System.out.println(pType.getActualTypeArguments()[0]);// class java.sql.Date
		
		return null;
	}
	public static void applyVector(Vector<Date> v1){}
}
 

 
总结

java中的泛型<T>是编译时行为,为了实现在编译时就避免类型转换异常,而又保持Object的抽象性


Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐