泛型一条街
参考张孝祥系列JDK 1.8 API场景为什么java后来要引入泛型?泛型原理与特性?如何自定义方法泛型与类泛型?如何通过反射获取参数化类型的实际类型参数分析package cool.pengych.java.generic;import java.lang.reflect.InvocationTargetException;import java.lang.ref
·
参考
张孝祥系列
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的抽象性
更多推荐
已为社区贡献2条内容
所有评论(0)