《Java核心技术卷I》泛型篇笔记(五) 泛型与反射

写在最前:本笔记全程参考《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接口的子类和子接口来获取泛型类型的声明:

  1. Class类:Type接口的子类,用来描述具体的类型
  2. TypeVariable接口:描述类型变量(如T extends Comparable<? super T>
  3. WildcardType接口:描述通配符(如? super T
  4. ParameterizedType接口:描述泛型类或接口类型(如Comparable<? super T>
  5. 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]
		 */
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值