夯实Spring系列|第二十一章:Spring 泛型处理(Generic Resolution)

夯实Spring系列|第二十一章:Spring 泛型处理(Generic Resolution)

本章说明

本章主要是介绍 Spring 中泛型的一些设计细节,以及一些关键 API 的使用。

1.项目环境

2.Java 泛型基础

泛型类型

  • 泛型类型是在类型上参数化的泛型类或者接口

泛型使用场景

  • 编译时强类型检查
  • 避免类型强转
  • 实现通用算法

泛型类型擦写

泛型被引入到 Java 语言中,以便在编译是提供更严格的类型检查并支持泛型编程。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。

为了实现泛型,编译器将类型擦除应用于:

  • 将泛型类型中的所有类型参数替换为其边界,如果类型参数是无边界的,则将其替换为 Object 。因此,生产的字节码只包含普通类、接口和方法
  • 必要时插入类型转换以保持类型安全
  • 生成桥接方法时以保留扩展泛型类型中的多态性

2.1 举例说明

集合场景

public interface Collection<E> extends Iterable<E> {
    ...
    boolean addAll(Collection<? extends E> c);
    ...
  • <? extends E> 可以保证参数的边界,E 或者 E 的子类,同时也确保的了多态性
  • E 表示插入元素类型,做一个类型的约束

2.2 代码示例

public class GenericDemo {
    public static void main(String[] args) {
        // Java 7 Diamond 语法
        Collection<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("World");
        // 编译时错误
//        list.add(1);

        // 通过下面的方式可以欺骗编译器
        // 泛型擦写
        Collection temp = list;
        // 编译通过
        temp.add(1);

        System.out.println(list);
    }
}

3.Java 5 类型接口

Java 5 类型接口 - java.lang.reflect.Type

派生类或接口说明
java.lang.ClassJava 类 API,如 java.lang.String
java.lang.reflect.GenericArrayType泛型数组类型
java.lang.reflect.ParameterizedType泛型参数类型
java.lang.reflect.TypeVariable泛型类型变量,Collection<E> 中的 E
java.lang.reflect.WildcardType泛型通配类型

Java 泛型反射 API

类型API
泛型信息(Generics Info)java.lang.Class#getGenericInfo
泛型参数(Parameters)java.lang.reflect.ParameterizedType
泛型父类(Super Classes)java.lang.Class#getGenericSuperclass
泛型接口(Interfaces)java.lang.Class#getGenericInterfaces
泛型声明(Generics Declaration)java.lang.reflect.GenericDeclaration

API 使用示例

public class GenericAPIDemo {
    public static void main(String[] args) {
        // 原生类型 primitive types :int long float ...
        Class intClass = int.class;

        // 数组类型 array types:int[],Object[]
        Class objectArrayClass = Object[].class;

        // 原始类型 raw types:java.lang.String
        Class rawClass = String.class;

        System.out.printf("原生类型:[%s]\n数组类型:[%s]\n原始类型:[%s]\n", intClass, objectArrayClass, rawClass);

        // 泛型参数类型 parameterized type
        ParameterizedType parameterizedType = (ParameterizedType)ArrayList.class.getGenericSuperclass();

        System.out.println(parameterizedType.toString());
        Type[] typeVariables = parameterizedType.getActualTypeArguments();
        Stream.of(typeVariables).map(TypeVariable.class::cast).forEach(System.out::println);
    }
}

执行结果:

原生类型:[int]
数组类型:[class [Ljava.lang.Object;]
原始类型:[class java.lang.String]
java.util.AbstractList<E>
E

4.Spring 泛型类型辅助类

  • 核心 API - org.springframework.core.GenericTypeResolver - Spring 2.5.2+
    • 处理类型(Type)相关方法
      • org.springframework.core.GenericTypeResolver#resolveReturnType
      • org.springframework.core.GenericTypeResolver#resolveType(java.lang.reflect.Type, java.lang.Class<?>)
    • 处理泛型参数类型(ParameterizedType)相关方法
      • org.springframework.core.GenericTypeResolver#resolveReturnTypeArgument
      • org.springframework.core.GenericTypeResolver#resolveTypeArgument
      • org.springframework.core.GenericTypeResolver#resolveTypeArguments
    • 处理泛型类型变量(TypeVariable)相关方法
      • getTypeVariableMap

API 操作示例

public class GenericTypeResolverDemo {
    public static void main(String[] args) throws NoSuchMethodException {
        Class clazz = GenericTypeResolverDemo.class;
        System.out.println("======1=======");
        displayReturnTypeGenericInfo(clazz, List.class, "getString", null);
        // String 是 Comparable 接口的具体化
        // public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
        System.out.println("======2=======");
        displayReturnTypeGenericInfo(clazz, Comparable.class, "getString", null);
        System.out.println("======3=======");
        displayReturnTypeGenericInfo(clazz, List.class, "getUsers", null);
        System.out.println("======4=======");
        displayReturnTypeGenericInfo(clazz, List.class, "getStringList", null);
        System.out.println("======5=======");
        displayReturnTypeGenericInfo(clazz, List.class, "getStringList2", null);

        // TypeVariable
        Map<TypeVariable, Type> typeVariableMap = GenericTypeResolver.getTypeVariableMap(StringList.class);
        System.out.println(typeVariableMap);
    }

    public String getString() {
        return "Hello,World";
    }

    public <E> List<E> getUsers() {
        return Collections.emptyList();
    }

    /**
     * 泛型参数的具体化
     *
     * @return
     */
    public List<String> getStringList() {
        return Collections.emptyList();
    }

    /**
     * 泛型参数的具体化
     *
     * @return
     */
    public StringList getStringList2() {
        return null;
    }

    /**
     * 打印返回类型泛型信息
     *
     * @param containingClass 所在的类
     * @param genericIfc      泛型接口
     * @param methodName      方法名称
     * @param arguments       参数
     * @throws NoSuchMethodException
     */
    private static void displayReturnTypeGenericInfo(Class<?> containingClass, Class<?> genericIfc, String methodName, Class... arguments) throws NoSuchMethodException {
        Method method = containingClass.getMethod(methodName, arguments);
        Class<?> returnType = GenericTypeResolver.resolveReturnType(method, containingClass);
        // 常规类作为方法返回值类型
        System.out.println("返回值的类型:" + returnType);
        // 常规类型不具备泛型参数类型
        Class<?> returnTypeArgument = GenericTypeResolver.resolveReturnTypeArgument(method, genericIfc);
        System.out.println("返回参数泛型类型:" + returnTypeArgument);
    }

}

执行结果

======1=======
返回值的类型:class java.lang.String
返回参数泛型类型:null
======2=======
返回值的类型:class java.lang.String
返回参数泛型类型:class java.lang.String
======3=======
返回值的类型:interface java.util.List
返回参数泛型类型:null
Disconnected from the target VM, address: '127.0.0.1:51313', transport: 'socket'
======4=======
返回值的类型:interface java.util.List
返回参数泛型类型:class java.lang.String
======5=======
返回值的类型:class com.huajie.thinking.in.spring.generic.StringList
返回参数泛型类型:class java.lang.String
{T=class java.lang.String, E=class java.lang.String, E=class java.lang.String, E=class java.lang.String, E=class java.lang.String, E=class java.lang.String}

通过结果我们可以得到以下结论:

  • 方法返回参数泛型类型必须是具体类型,才能通过 resolveReturnTypeArgument 取到相关信息
  • 比如 List<String>
  • 或者 class StringList extends ArrayList<String>
  • 或者 public final class String implements Comparable<String>

5.Spring 泛型集合类型辅助类

核心 API - GenericCollectionTypeResolver - 该类Spring5.0 之后就被移除了,我们这里就不展开了,替换实现 ResolvableType,下面会进行讨论。

6.Spring 方法参数封装 - MethodParameter

核心 API - org.springframework.core.MethodParameter

  • 起始版本 Spring 2.0+
  • 元信息
    • 关联的方法 - Method
    • 关联的构造器 - Constructor
    • 构造器或方法参数索引 - parameterIndex
    • 构造器或方法参数类型 - parameterType
    • 构造器或方法参数泛型类型 - genericParameterType
    • 构造器或方法参数名称 - parameterName
    • 所在的类 - containingClass

7.Spring 4.2 泛型优化实现 - ResolvableType

核心 API - org.springframework.core.ResolvableType

  • 起始版本 Spring 4.0+
  • 扮演角色:GenericTypeResolver 和 GenericCollectionTypeResolver 替代者
  • 工厂方法:for* 方法
  • 转换方法:as* 方法
  • 处理方法:resolve* 方法

Java Doc 中的示例

    private HashMap<Integer, List<String>> myMap;

    public void example() throws NoSuchFieldException {
        ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
        t.getSuperType(); // AbstractMap<Integer, List<String>>
        t.asMap(); // Map<Integer, List<String>>
        t.getGeneric(0).resolve(); // Integer
        t.getGeneric(1).resolve(); // List
        t.getGeneric(1); // List<String>
        t.resolveGeneric(1, 0); // String
    }

使用上个例子中的 StringList 来操作

public class ResolvableTypeDemo {
    public static void main(String[] args) {
        // 工厂创建
        ResolvableType resolvableType = ResolvableType.forClass(StringList.class);

        resolvableType.getSuperType();// ArrayList
        resolvableType.getSuperType().getSuperType();// AbstractList

        System.out.println(resolvableType.asCollection().resolve()); // 获取 Raw 类型
        System.out.println(resolvableType.asCollection().resolveGeneric(0));// 获取泛型参数类型

    }
}

测试结果:

interface java.util.Collection
class java.lang.String

8.ResolvableType 的局限性

局限一:ResolvableType 无法处理泛型擦写

局限二:ResolvableType 无法处理非具体化的 ParameterizedType

9.面试

9.1 Java 泛型擦写发生在编译时还是运行时?

运行时

9.2 Java 5 Type 类型的派生类或者接口有哪些?

第 3 小节 Java 5 类型接口

10.参考

  • 极客时间-小马哥《小马哥讲Spring核心编程思想》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值