Java泛型反射
基本上所有现代化语言都有泛型实现, 最大的实现就是容器对象:
Set<T>List<T>Map<KEY,VALUE>
注入此类容器对象, 这种类型在 Java 当中虽然有 List<Object> 和 List<T> 两种方式, 但是请注意在 Object 是全局对象类型!
(Object value = 100) 和 (int value = 100)两者一个是对象类型, 一个是值类型需要区分出来, 后续编译器则是直接帮助值类型转化为对象类型.
如果没有泛型反射, 那么需要处理不同类型如:
List<String>List<Integer>List<Long>- …其他相关类型扩展
后续衍生的泛型就是为了这样处理从而生成模板代码:
/**
* T 就是泛型类模板, 用于替换成自定义泛型, 也就是 MyList<String>/ MyList<Integer> 都能自动生成.
*/
public class MyList<T> {
private T[] array;
private int size;
}
这样就能直接通过 MyList<String> myList = new MyList<String>() 生成模板代码, 编写一次万能匹配,
可以通过编译器保证了类型安全: 这就是 泛型.
这种泛型推断可以方便快捷衍生出集成接口:
/**
* 比较接口, 要求对数组实现
* @param <T>
*/
public interface Comparable<T> {
/**
* 返回负数: 当前实例比参数o小
* 返回0: 当前实例与参数o相等
* 返回正数: 当前实例比参数o大
*/
int compareTo(T o);
}
// 这样就能对自己的自定义类对象处理比较大小操作
public class SelfClass implements Comparable<SelfClass> {
private int value;
/**
* 让其去比较两者
* @param other 其他实例
* @return int
*/
public int compareTo(SelfClass other) {
return this.value.compareTo(other.value);
}
}
这样就能对类对象来做自定义泛型处理, 十分简单方便.
注意: 泛型类型不能用于静态方法, 也就是无法静态化构建出泛型类型, 但是以下代码可以通过:
public class Message<T> {
private final T msg;
public Message(T msg) {
this.msg = msg;
}
public T1 getMsg() {
return msg;
}
// 这里是可以通过的
public static <T> Message<T> build(T msg) {
return new Message<>(msg);
}
}
这是因为 <T> 和 Message<T> 不算是同个类型, <T> 实际上算是 T2泛型类型, 用代码声明就类似:
public class Message<T1> {
private final T1 msg;
public Message(T1 msg) {
this.msg = msg;
}
public T1 getMsg() {
return msg;
}
// 实际上这里是又声明泛型类型T2, 和 T1 的泛型类型没有关系.
public static <T2> Message<T2> build(T2 msg) {
return new Message<>(msg);
}
}
对于 Java 当中的泛型, 默认采用 Object 安装转化:
// 以上面的类型声明实例, 我们看到代码如下:
Message<String> msg = new Message<>("hello.world");
String info = msg.getMsg();
// 但是虚拟机处理却是以下情况, 虚拟机并没有泛型概念:
Message msg = new Message("hello.world");
// 默认内部泛型类型最会归为 Object 对象
// 只是取出来的时候被内部做强制Object转化而已
String info = (String)msg.getMsg();
这里也就衍生出 Java 内部泛型的 类型擦除, 也就是虚拟机在构建程序时候会把泛型类转化为全局 Object 后转化为指定类型:
- 不能为值类型/基本类型:
Message<int> msg = new Message<>(123);这是错误的, 因为内部必须是最高Object对象衍生. - 无法获取泛型的
Class分类:Message<Integer> == Message<String> == Message.class == Message<Object>.class默认都是归属同个Message<Object>.class - 无法判断泛型实力的类型:
Message<Integer> msg = new Message<>(123);该实例无法通过if(msg instanceof Pair<String>)识别是否是指定类型. - 无法
new实例化泛型:T t1 = new T()这种方式是错误的, 因为被内部擦除之后变成代码为Object t1 = new Object(), 这种代码明显是无法通过执行的. - 注意异常覆写方式:
equals(T t)这种方式和equals(Object t)不能被声明, 因为Object默认已经涵盖这个方法, 泛型无法做重新覆盖处理.
当然在 Java 泛型是允许被衍生声明的只允许指定实现类的:
// 声明主要的泛型类
public class Message<T> {
public T msg;
}
// 衍生出只要 String 的具体类
public class StringMessage extends Message<String> {
}
// 衍生出只要 Integer 的具体类
public class IntegerMessage extends Message<Integer> {
}
public class Executor {
// 限定要求必须是 String 类型的泛型实例
public static void strMsg(Message<? extends String> msg) {
String ctx = msg.msg;
}
}
这种 <?> 语法默认是函数方法泛型声明语法, 用于标识运行时才可知类型, 同时也可以要求指定某些实现 <? extends String>
要求类型必须是继承了 String 对象或者 String 本身:
// 表面上代码处理
void strMsg(Message<? extends String> msg);
// 实际上虚拟机擦除类型之后, 哪怕是 String 类型也因为里氏替换原则被父类捕获到
void strMsg(Message<String> msg);
除了 <?> 和 <? extends T> 之外, 还有 <? super T> 向上取类型方式:
// 这里传入的 msg 限定了传入类型必须是继承过 Integer 的类对象
// 这时候是处于向下获取子类的检索类型
void intMsg(Message<? extends Integer> msg);
// 但是 Integer 之上还有父类 Number, 现在要求不是向下类型检索, 而是向上
// 也就是要求传入 Integer 往上类型而非往下泛型
void setMsg(Message<? super Integer> msg);
// 对于虚拟机来说, 擦除类型之后函数方法签名为:
void setMsg(? super Integer);
// 所以需要传入的修改类型, 是可以被接受的, 因为 Number 类型就是 Integer 的上级对象.
Number value = value;
setMsg(value);
注意: <? extends T> 只能使用在被读取而不能被写入情况; 而 <? super T> 只能用于允许写入而不能读取情况.
<? extends T>允许调用读方法T get()获取T的引用, 但不允许调用写方法set(T)传入T的引用(传入null除外)<? super T>允许调用写方法set(T)传入T的引用, 但不允许调用读方法T get()获取T的引用(获取Object除外)
Java标准库实现就能看到具体实践:
public class Collections {
// 把src的每个元素复制到dest中, 注意两个 extend 和 super 的使用
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
T t = src.get(i);
dest.add(t);
}
}
}
要关键留意:
extends负责读取,super只允许写入
反射
Java 内部泛型衍生以下具体类系统结构:
ClassParameterizedTypeGenericArrayTypeWildcardType
在默认获取对象类型来说:
// 获取类泛型, Class<T> 就是泛型
Class<String> clazz = String.class;
// 这里就相当于实例化, new 无法直接实例化, 只能依靠这样直接替代实例化
String instance = clazz.newInstance();
// 调用Class的 getSuperclass() 方法返回的Class类型是 Class<? super T>, 也就是可写入对象
Class<? super String> sup = String.class.getSuperclass();
// 完整生成链
Class<Integer> clazz = Integer.class;
Constructor<Integer> cons = clazz.getConstructor(int.class);
Integer i = cons.newInstance(123);
注意: 新版本 Java 安全升级之后 Class.newInstance() 这个实例化版本已经淘汰, 需要升级:
// 老版本
Class<String> clazz = String.class;
String instance = clazz.newInstance();
// 新版本, 要求 getConstructor 设置获取构造器之后实例化处理
Class<String> clazz = String.class;
Constructor<?> constructor = clazz.getConstructor(String.class);
Object instance = constructor.newInstance("Hello, world!");
同时需要注意, 不允许以 new 生成带泛型的数组:
// 这里是成功的, 因为类型擦除时候为 Message<Object>[] arr = null
Message<String>[] arr = null
// 但是直接 new 是直接错误的, 以下代码直接编写会错误
Message<String>[] arr = new Message<>[2];
// 擦除类型, 之后 Object 并不清除什么类型需要怎么分配, 直接就会异常
Message<Object>[] arr = new Message<Object>[2];
// 可以通过 @SuppressWarnings("unchecked") + 强制转化来绕过安全检查
Message<String>[] arr = (Message<String>[])new Message[2];
泛型数组处理要极其小心, 很容易会出现对象数据内存越界的情况, 特别是
千万不要把内部创建泛型数组传递给外部使用.