泛型基础
认识泛型
集合如 ArrayList 在没有泛型的时候,add函数接收一个Object型的参数,取出的也是Object
每次需要类型转换,很麻烦 如果往一个String 的集合中添加了 Date
对象,编译是不会报错的,但这不是我们
ArrayList list = new ArrayList();
list.add(1);
list.add("Sam");
list.add(false);
list.add(new Date());
Object o = list.get(0);
int obj = (int) o;
System.out.println(obj);
String objStr = (String) list.get(1);
System.out.println(objStr);
boolean flag = (boolean) list.get(2);
System.out.println(flag);
Date date = (Date) list.get(3);
System.out.println(date);
使用泛型:泛型是针对编译器有效,规定泛型类型是什么。如在泛型是 String
的集合中添加Date
对象,编译器报错
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Sam");
// 编译不过
// arrayList.add(new Date());
泛型类
所谓泛型类(generic class)就是具有一个或多个类型参数的类 注意,基本类型不能作为泛型类型 示例:
// 一般的类
public class GeneralClazz {}
// 泛型类,参数类型为 T、K
public class GenericClazz<T> {}
public class GenericClazz<T, K> {}
泛型类实例化:把泛型的类型参数换成具体的类型。一个类可以得到最大的复用。
GenericClazz<String> clazz = new GenericClazz<String>();
GenericClazz<Integer> clazz = new GenericClazz<Integer>();
泛型方法
泛型方法,就是带有类型参数的方法,它既可以定义在泛型类中,也可以定义在普通类中 如果某个类中多个方法使用泛型,那么建议使用类级别的泛型(定义为泛型类)
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
静态方法的泛型
泛型类中的静态方法不能用泛型
静态方法不需要创建对象就可以使用,在示例之前 泛型类却规定了对象的泛型,一定要实例化才可以使用
public class GenericityClazz<T> {
// 编译报错
public static void staticSet(T t) {}
}
静态方法使用泛型:独立于所属的类,单独存在
public class GenericityClazz<T> {
// 编译报错
public static void staticSet(T t) {}
// 静态方法是独立的
// 该方法的泛型与这个泛型类的泛型不是同一个
public static <T> void staticSet(T t){}
}
参数化类型与原始类型的兼容性
参数化类型与原始类型的对象可以相互引用
// 参数化类型可以引用一个原始类型的对象,但是编译器会警告
ArrayList<String> c = new ArrayList();
// 原始类型可以引用一个参数化类型的对象
ArrayList c2 = new ArrayList<String>();
参数化类型不考虑类型参数的继承关系
// 编译器报错:原因在于 v 中拿到的是String,而实际上ArrayList中则为Object,其中有可能是 Date
// ArrayList<String> v = new ArrayList<Object>();
// 编译器报错
// ArrayList<Object> v2 = new ArrayList<String>();
创建数组实例时,数组的元素不能使用参数化的类型
// 编译器报错
ArrayList<Integer> v3[] = new ArrayList<Integer>[10];
泛型的通配符
通配符 ?
定义一个方法,打印出任意参数化类型的集合
// 只能打印Integer类型的
public static void printCollection(Collection<Integer> collection) {}
// 由于 参数化类型不考虑类型参数的继承关系,所以编译报错
public static void printCollection2(Collection<Object> collection) {}
使用通配符 ? ,表示任意类型
public static void printCollection3(Collection<?> collection) {
for (Object o : collection) {
System.out.println(o);
}
}
// 使用guava 的 Lists 工具类
ArrayList<Integer> list = Lists.newArrayList(1, 2); // Integer类型
ArrayList<String> list2 = Lists.newArrayList("sam", "rabby"); // String 类型
// 调用
printCollection3(list);
printCollection3(list2);
通配符方法注意问题
? 通配符定义的变量主要用于引用,是不确定类型的 可以调用与参数化无关的方法,不能调用与参数化有关的方法
public static void printCollection3(Collection<?> collection) {
// 编译报错:? 是不确定类型,如果collection参数类型是String,add()一个Integer就有问题
collection.add(1);
// 正常运行
collection.size();
}
通配符的边界:对?进行限制,限定通配符总是包括自己
限定通配符的上边界,? extends Number
,意思为Number
或Number
的子类
ArrayList<? extends Number> = new ArrayList<Integer>()
限定通配符的下边界,? super Integer
,意思为Integer
或Integer
的父类
ArrayList<? super Integer> = new ArrayList<Number>()
通配符的方法可以改造
public static void printCollection3(Collection<? extends Number> collection) {
for (Object o : collection) {
System.out.println(o);
}
}
// Integer类型
ArrayList<Integer> list = Lists.newArrayList(1, 2);
printCollection3(list);
// String 类型,编译不过
ArrayList<String> list2 = Lists.newArrayList("sam", "rabby");
printCollection3(list2);
PECS 原则:Producer Extends, Consumer Super
Producer Extends
:如果你需要一个只读 List,用它来 produce T,那么使用 ? extends T
Consumer Super
:如果你需要一个只写 List,用它来 consume T,那么使用 ? super T
class Reader<T> {
T readExact(List<T> list) {
return list.get(0);
}
/**
* 接受的参数只要是满足Fruit的子类就行(包括Fruit自身),这样子类和父类之间的关系也就关联上了
* “Producer Extends” – 如果你需要一个只读List,用它来produce T,那么使用? extends T。
*
* @param list
* @return
*/
T readCovariant(List<? extends T> list) {
return list.get(0);
}
/**
* “Consumer Super” – 如果你需要一个只写List,用它来consume T,那么使用? super T。
*
* @param list
* @param item
*/
void writeWithWildcard(List<? super T> list, T item) {
list.add(item);
}
}
JDK 源码中PECS原则使用: Collections
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
泛型擦除(泛型的去类型化)
泛型是提供给编译器使用的, 但是编译器编译以后会去掉类型信息
,使程序运行不受影响 对于各个参数化的泛型类型,getClass()
方法的返回值和原始类型完全一样
// 两个不同泛型的集合,但是字节码是一样的
ArrayList<String> list = new ArrayList<String>();
ArrayList<Integer> list1 = new ArrayList<Integer>();
//true 说明是同一份字节码
System.out.println(list.getClass() == list1.getClass());
由于编译器生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,如用反射得到集合,再调用其add
方法
// 两个不同泛型的集合,但是字节码是一样的
ArrayList<String> list = new ArrayList<String>();
ArrayList<Integer> list1 = new ArrayList<Integer>();
// 往泛型为Integer的集合中放入String的值,编译肯定不过,但是可以利用反射跳过编译器,实现
// list1.add("ababab");
// 利用反射实现
list1.getClass().getMethod("add", Object.class).invoke(list1, "现在就可以传字符串了");
// 本来是 Integer 的类型,但是现在却成功加入了 String
System.out.println(list1);
// 获取的时候,编译器会返回 Integer,实际应该是字符串了,编译正确,运行会报错
Integer a = list1.get(0);
System.out.println(list1.get(0));
输出:
[现在就可以传字符串了]
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
泛型反射
获取泛型的方法:ParameterizedType.getActualTypeArguments()
示例
// getGenericSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。
Type genericSuperclass = clazz.getGenericSuperclass();
print(genericSuperclass instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
// 获取泛型的类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Type actualTypeArgument = actualTypeArguments[0];
System.out.println(actualTypeArgument.getTypeName());
// 返回的是当前这个 ParameterizedType 的类型
Type rawType = parameterizedType.getRawType();
// Type getOwnerType()返回 Type 对象,表示此类型是其成员之一的类型。例如,如果此类型为 O<T>.I<S>,则返回 O<T> 的表示形式。
// 如果此类型为顶层类型,则返回 null。
Type ownerType = parameterizedType.getOwnerType();
通用的泛型获取方法
package com.learning.optimize.jdk.genericity;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* ClassName: GenericsUtils
* Description: 泛型工具类
* Date: 2018/7/18 14:19 【需求编号】
*
* @author Sam Sho
* @version V1.0.0
*/
public class GenericsUtils {
/**
* 通过反射,获得指定类的父类的泛型参数的实际类型. 如BuyerServiceBean extends DaoSupport<Buyer>
*
* @param clazz clazz 需要反射的类,该类必须继承范型父类
* @param index 泛型参数所在索引,从0开始.
* @return 范型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回<code>Object.class</code>
*/
@SuppressWarnings("unchecked")
public static Class getSuperClassGenericType(Class clazz, int index) {
//得到泛型父类
Type genType = clazz.getGenericSuperclass();
//如果没有实现ParameterizedType接口,即不支持泛型,直接返回Object.class
if (!(genType instanceof ParameterizedType)) {
return Object.class;
}
//返回表示此类型实际类型参数的Type对象的数组,数组里放的都是对应类型的Class, 如BuyerServiceBean extends DaoSupport<Buyer,Contact>就返回Buyer和Contact类型
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数"));
}
if (!(params[index] instanceof Class)) {
return Object.class;
}
return (Class) params[index];
}
/**
* 通过反射,获得指定类的父类的第一个泛型参数的实际类型. 如BuyerServiceBean extends DaoSupport<Buyer>
*
* @param clazz clazz 需要反射的类,该类必须继承泛型父类
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回<code>Object.class</code>
*/
@SuppressWarnings("unchecked")
public static Class getSuperClassGenericType(Class clazz) {
return getSuperClassGenericType(clazz, 0);
}
/**
* 通过反射,获得方法返回值泛型参数的实际类型. 如: public Map<String, Buyer> getNames(){}
*
* @param method 方法
* @param index 泛型参数所在索引,从0开始.
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回<code>Object.class</code>
*/
@SuppressWarnings("unchecked")
public static Class getMethodGenericReturnType(Method method, int index) {
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType) returnType;
Type[] typeArguments = type.getActualTypeArguments();
if (index >= typeArguments.length || index < 0) {
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数"));
}
return (Class) typeArguments[index];
}
return Object.class;
}
/**
* 通过反射,获得方法返回值第一个泛型参数的实际类型. 如: public Map<String, Buyer> getNames(){}
*
* @param method 方法
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回<code>Object.class</code>
*/
@SuppressWarnings("unchecked")
public static Class getMethodGenericReturnType(Method method) {
return getMethodGenericReturnType(method, 0);
}
/**
* 通过反射,获得方法输入参数第index个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String, Buyer> maps, List<String> names){}
*
* @param method 方法
* @param index 第几个输入参数
* @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回空集合
*/
@SuppressWarnings("unchecked")
public static List<Class> getMethodGenericParameterTypes(Method method, int index) {
List<Class> results = new ArrayList<Class>();
Type[] genericParameterTypes = method.getGenericParameterTypes();
if (index >= genericParameterTypes.length || index < 0) {
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数"));
}
Type genericParameterType = genericParameterTypes[index];
if (genericParameterType instanceof ParameterizedType) {
ParameterizedType aType = (ParameterizedType) genericParameterType;
Type[] parameterArgTypes = aType.getActualTypeArguments();
for (Type parameterArgType : parameterArgTypes) {
Class parameterArgClass = (Class) parameterArgType;
results.add(parameterArgClass);
}
return results;
}
return results;
}
/**
* 通过反射,获得方法输入参数第一个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String, Buyer> maps, List<String> names){}
*
* @param method 方法
* @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回空集合
*/
@SuppressWarnings("unchecked")
public static List<Class> getMethodGenericParameterTypes(Method method) {
return getMethodGenericParameterTypes(method, 0);
}
/**
* 通过反射,获得Field泛型参数的实际类型. 如: public Map<String, Buyer> names;
*
* @param field 字段
* @param index 泛型参数所在索引,从0开始.
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回<code>Object.class</code>
*/
@SuppressWarnings("unchecked")
public static Class getFieldGenericType(Field field, int index) {
Type genericFieldType = field.getGenericType();
if (genericFieldType instanceof ParameterizedType) {
ParameterizedType aType = (ParameterizedType) genericFieldType;
Type[] fieldArgTypes = aType.getActualTypeArguments();
if (index >= fieldArgTypes.length || index < 0) {
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数"));
}
return (Class) fieldArgTypes[index];
}
return Object.class;
}
/**
* 通过反射,获得Field泛型参数的实际类型. 如: public Map<String, Buyer> names;
*
* @param field 字段
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回<code>Object.class</code>
*/
@SuppressWarnings("unchecked")
public static Class getFieldGenericType(Field field) {
return getFieldGenericType(field, 0);
}
}
参考
源码地址:Java 进阶 java进阶(五):Java泛型 Java 泛型详解 Java泛型详解