写在最前:本笔记全程参考《Java核心技术卷I》,添加了一些个人的思考和整理
目录
反射与泛型
1. 泛型Class类
Class
类是泛型的,例如,String.class
是一个Class<String>
的对象(且是唯一的对象)
这个类型参数分词有用,它允许Class<T>
的方法返回特定的数据类型。
newInstance
方法返回一个这个类的实例对象,由无参数构造器获取。它的返回值类型被声明为T,与CLass中声明的参数类型相同,这样就避免了类型转换。
2. 使用Class泛型类参数创建对象
可以利用Class<T>
、通过反射创建对象:
public static <T> T getObj(Class cl) {
return cl.getConstructor().newInstance();
}
3. 获取类的泛型
ParameterizedType parametclass =
(ParameterizedType) this.getClass().getGenericSuperclass();
Type[] actualTypeArguments = parametclass.getActualTypeArguments();
clazz = (Class<T>) actualTypeArguments[0];
4. 虚拟机中的泛型类型信息
泛型擦除后,类依然会保留原先泛型的微弱记忆。例如,原始的Pair
类知道它源自于泛型类Pair<T>
,尽管一个Pair
类型的对象无法区分它是构造为Pair<String>
还是Pair<Employee>
。运行时,我们仍可以借助反射来获取泛型的信息。
Type接口的子类和子接口
可以借助Type接口的子类和子接口来获取泛型类型的声明:
Class
类:Type接口的子类,用来描述具体的类型TypeVariable
接口:描述类型变量(如T extends Comparable<? super T>
)WildcardType
接口:描述通配符(如? super T
)ParameterizedType
接口:描述泛型类或接口类型(如Comparable<? super T>
)GenericArrayType
接口:描述泛型数组(如T[]
)
虚拟机将自动实例化这些接口适当的类
下图是Type接口与它的子类或子接口的继承关系
5. 类型字面量
有些时候,我们希望对于不同的类型参数,同一个类有不同的行为。通常的实现方法是将Class对象与某个动作管理
例如,我们希望ArrayList<Integer>
和ArrayList<Character>
都有自己独特的方法。但是,类型擦除后两者都会转换为ArrayList原始类型。这时怎么再根据泛型来选择不同的方法呢?
思路是,借助Type接口,将泛型的信息保存为字面量,之后在通过比较这个字面量来选择方法。
原书上的代码清单8-5
《Java核心技术卷I》第11版p360 提供了一个程序清单:
——程序清单8-5
genericReflection/TypeLiterals.class
核心想法就是:实现了一个借助类型字面量来匹配泛型类的不同方法的一个匹配器,将类型与格式化函数关联起来。
它的思路是创建一个匹配器类(例子中是Formatter,意为格式化器,它会根据不同的类型参数来采取不同的格式化策略),通过HashMap
储存类型和方法的对应关系,然后调用匹配器类提供的方法来执行类型对于的方法。代码内容比较多也比较复杂,我把代码原封不动的抄下来了,能否理解就看你们的啦
TypeLiteral<T>
类
/**
* @description 描述了一个可以是泛型的类型,例如ArrayList
*/
class TypeLiteral<T> {
private Type type;
/**
* 这个构造函数必须以匿名子类的形式调用,例如 new formatter.TypeLiteral<...>(){}
*/
public TypeLiteral() {
Type parentType = getClass().getGenericSuperclass();
if (parentType instanceof ParameterizedType) {
type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
} else {
throw new RuntimeException(
"应该通过 “new formatter.TypeLiteral<...>(){};” 的形式构造实例"
);
}
}
private TypeLiteral(Type type) {
this.type = type;
}
/**
* 生成描述给定类型的类型文字
*/
public static TypeLiteral<?> of(Type type) {
return new TypeLiteral<Object>(type);
}
@Override
public String toString() {
if (type instanceof Class) {
return ((Class<T>) type).getName();
} else {
return type.toString();
}
}
@Override
public int hashCode() {
return type.hashCode();
}
@Override
public boolean equals(Object otherObj) {
return
(otherObj instanceof TypeLiteral) &&
(type.equals(((TypeLiteral<?>) otherObj).type));
}
}
Formatter
类
public class Formatter {
private Map<TypeLiteral<?>, Function<?, String>> rules = new HashMap<>();
/**
* 为这个Formatter对象添加格式化规则(formatting rule)
* @param type 传入应用此规则的类型
* @param formatterForType type类型的对应函数
* @param <T>
*/
public <T> void forType(TypeLiteral<T> type, Function<T, String> formatterForType) {
rules.put(type, formatterForType);
}
/**
* formatField会针对不同的类型执行不同的方法
* @return 一个包含对象的所有字段的字符串,针对不同的类型,
会对字符串进行不同的处理(格式化)
*/
public String formatField(Object obj) throws IllegalArgumentException, IllegalAccessException {
StringBuilder result = new StringBuilder();
for (Field f : obj.getClass().getDeclaredFields()) {
result.append(f.getName());
result.append("=");
f.setAccessible(true);
// 获取类型对应的方法
Function<?, String> formatterForType =
rules.get(TypeLiteral.of(f.getGenericType()));
if (formatterForType != null) {
@SuppressWarnings("unchecked")
Function<Object, String> objectFormatter =
(Function<Object, String>) formatterForType;
result.append(objectFormatter.apply(f.get(obj)));
} else {
result.append(f.get(obj).toString());
}
result.append("\n");
}
return result.toString();
}
}
TypeLiteralsTest
测试类
/**
* @description 测试类
*/
public class TypeLiteralsTest {
// 样本数据
public static class Sample {
ArrayList<Integer> nums;
ArrayList<Character> chars;
ArrayList<String> strings;
public Sample() {
nums = new ArrayList<>();
nums.add(42);
nums.add(1729);
chars = new ArrayList<>();
chars.add('H');
chars.add('i');
strings = new ArrayList<>();
strings.add("Hello");
strings.add("World");
}
}
private static <T> String join(String separator, ArrayList<T> elements) {
StringBuilder result = new StringBuilder();
for (T e : elements) {
if (result.length() > 0) {
result.append(separator);
}
result.append(e.toString());
}
return result.toString();
}
public static void main(String[] args) throws IllegalAccessException {
Formatter formatter = new Formatter();
// 为Integer、Character定义独特的方法
formatter.forType(
new TypeLiteral<ArrayList<Integer>>(){},
lst -> join(" ", lst));
formatter.forType(
new TypeLiteral<ArrayList<Character>>(){},
lst -> "\"" + join("", lst) + "\"");
System.out.println(formatter.formatField(new Sample()));
/*
结果:
nums=42 1729
chars="Hi"
strings=[Hello, World]
*/
}
}