当前位置: 首页 > news >正文

Spring泛型处理源码详解,Java泛型处理

文章目录

  • 一、Java 泛型基础
    • 1、泛型类型擦写
      • 代码实例
    • 2、Java 5 类型接口
      • 源码分析
      • 代码实例
    • 3、更多java泛型相关基础
  • 二、Spring旧版本泛型处理
    • 1、Spring 泛型类型辅助类
      • 代码实例
    • 2、Spring 泛型集合类型辅助类
      • 代码实例
    • 3、Spring 方法参数封装
  • 三、Spring 4.0泛型优化实现
    • 1、ResolvableType
      • 源码分析
      • 代码实例
    • 2、ResolvableType 的局限性
    • 3、ResolvableType 的设计优势
  • 参考资料

一、Java 泛型基础

泛型类型:泛型类型是在类型上参数化的泛型类或接口。

泛型使用场景:
• 编译时强类型检查
• 避免类型强转
• 实现通用算法

1、泛型类型擦写

泛型被引入到 Java 语言中,以便在编译时提供更严格的类型检查并支持泛型编程。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。为了实现泛型,编译器将类型擦除应用于:
• 将泛型类型中的所有类型参数替换为其边界,如果类型参数是无边界的,则将其替换为“Object”。因此,生成的字节码只包含普通类、接口和方法
• 必要时插入类型转换以保持类型安全
• 生成桥方法以保留扩展泛型类型中的多态性

代码实例

List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");

// 1.将有泛型约束的list赋值给没有泛型约束的一个新集合
List temp = list;

// 2.此时temp可以添加任意对象到集合中
temp.add(2);

// 3.使用temp获取集合中的元素可以正常获取,没有泛型约束意味着参数类型无边界
// 则将其泛型替换为Object类型
System.out.println(temp.get(2));

// 4.使用list获取集合中的元素会抛出类型转换异常,因为list集合的泛型为String类型
// 在获取元素时,会进行一次类型转换将元素转为String类型,所以会报错
System.out.println(list.get(2));

Java泛型 更多的是编译期间给我们做的强类型约束,而运行时JVM不会关心泛型的类型。

2、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
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

源码分析

我们看一下Class类,实现了Type接口。

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {

Type接口在JDK1.8之前,是一个空的接口,表示Java编程语言中所有类型的公共超接口。这些类型包括原始类型、参数化类型、数组类型、类型变量和基本类型。1.8之后增加了一个方法:

public interface Type {

    default String getTypeName() {
        return toString();
    }
}

Class中有着很多对泛型操作的方法,获取的泛型类型就是Type类型。

代码实例

// 基本类型 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;

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

//  parameterizedType.getRawType() = java.util.AbstractList

// 泛型类型变量 Type Variable:

System.out.println(parameterizedType.toString()); // java.util.AbstractList<E>

// <E>
Type[] typeVariables = parameterizedType.getActualTypeArguments();

Stream.of(typeVariables)
        .map(TypeVariable.class::cast) // Type -> TypeVariable
        .forEach(System.out::println);

java反射获取泛型此处只做简单实例。

3、更多java泛型相关基础

更多java泛型相关基础请自行学习,本文主要介绍Spring对泛型的处理。

二、Spring旧版本泛型处理

1、Spring 泛型类型辅助类

核心 API - org.springframework.core.GenericTypeResolver

版本支持:[2.5.2 , )

处理类型相关(Type)相关方法
• resolveReturnType
• resolveType

处理泛型参数类型(ParameterizedType)相关方法
• resolveReturnTypeArgument
• resolveTypeArgument
• resolveTypeArguments

处理泛型类型变量(TypeVariable)相关方法
• getTypeVariableMap

代码实例


import org.springframework.core.GenericTypeResolver;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * {@link GenericTypeResolver} 示例
 */
public class GenericTypeResolverDemo {

    public static void main(String[] args) throws NoSuchMethodException {

        // String 是 Comparable<String> 具体化
        displayReturnTypeGenericInfo(GenericTypeResolverDemo.class, Comparable.class, "getString");

        // ArrayList<Object> 是 List 泛型参数类型的具体化
        displayReturnTypeGenericInfo(GenericTypeResolverDemo.class, List.class, "getList");

        // StringList 也是 List 泛型参数类型的具体化
        displayReturnTypeGenericInfo(GenericTypeResolverDemo.class, List.class, "getStringList");

        // 泛型参数类型的具体化 具备 ParameterizedType 返回,否则 null

        // TypeVariable
        Map<TypeVariable, Type> typeVariableMap = GenericTypeResolver.getTypeVariableMap(StringList.class);
        // {E=class java.lang.String, E=class java.lang.String, T=class java.lang.String, E=class java.lang.String, E=class java.lang.String, E=class java.lang.String}
        // StringList 继承父类包括父接口列表,所以会有多个 E,并且同时指向了 java.lang.String 类对象
        // typeVariableTypeMap  包含了所有接口中的 TypeVariable 和 Type 的映射,简单地说,E 和 T 这类的,代表泛型参数,Type 代表具体的类型。
        System.out.println(typeVariableMap);
    }


    public static StringList getStringList() {
        return null;
    }

    public static ArrayList<Object> getList() { // 泛型参数类型具体化
        return null;
    }

    public static String getString() {
        return null;
    }

    /**
     *
     * @param containingClass Class类型
     * @param genericIfc 泛型接口类型
     * @param methodName 方法名
     * @param argumentTypes 方法参数
     * @throws NoSuchMethodException
     */
    private static void displayReturnTypeGenericInfo(Class<?> containingClass, Class<?> genericIfc, String methodName, Class... argumentTypes) throws NoSuchMethodException {

        // 根据方法名获取Method
        Method method = containingClass.getMethod(methodName, argumentTypes);

        // 声明类 GenericTypeResolverDemo.class 获取返回值类型
        Class<?> returnType = GenericTypeResolver.resolveReturnType(method, containingClass);

        // 常规类作为方法返回值
        System.out.printf("GenericTypeResolver.resolveReturnType(%s,%s) = %s\n", methodName, containingClass.getSimpleName(), returnType);
        // 常规类型不具备泛型参数类型 List<E> 获取返回值类型的泛型
        Class<?> returnTypeArgument = GenericTypeResolver.resolveReturnTypeArgument(method, genericIfc);
        System.out.printf("GenericTypeResolver.resolveReturnTypeArgument(%s,%s) = %s\n", methodName, containingClass.getSimpleName(), returnTypeArgument);

    }

}

class StringList extends ArrayList<String> { // 泛型参数具体化(字节码有记录)
}

2、Spring 泛型集合类型辅助类

核心 API - org.springframework.core.GenericCollectionTypeResolver

版本支持:[2.0 , 4.3] (新版本的spring已经不用了)

替换实现:org.springframework.core.ResolvableType

处理 Collection 相关
• getCollection*Type

处理 Map 相关
• getMapKeyType
• getMapValue
Type

代码实例


import org.springframework.core.GenericCollectionTypeResolver;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * {@link GenericCollectionTypeResolver} 示例
 */
public class GenericCollectionTypeResolverDemo {

    private static StringList stringList;

    private static List<String> strings;

    public static void main(String[] args) throws Exception {

        // StringList extends ArrayList<String> 具体化
        // getCollectionType 返回具体化泛型参数类型集合的成员类型 = String
        System.out.println(GenericCollectionTypeResolver.getCollectionType(StringList.class));

        System.out.println(GenericCollectionTypeResolver.getCollectionType(ArrayList.class));

        // 获取字段
        Field field = GenericCollectionTypeResolverDemo.class.getDeclaredField("stringList");
        System.out.println(GenericCollectionTypeResolver.getCollectionFieldType(field));

        field = GenericCollectionTypeResolverDemo.class.getDeclaredField("strings");
        System.out.println(GenericCollectionTypeResolver.getCollectionFieldType(field));
    }
}

3、Spring 方法参数封装

核心 API - org.springframework.core.MethodParameter

起始版本:[2.0 , )

元信息(部分)
• 关联的方法 - Method
• 关联的构造器 - Constructor
• 构造器或方法参数索引 - parameterIndex
• 构造器或方法参数类型 - parameterType
• 构造器或方法参数泛型类型 - genericParameterType
• 构造器或方法参数参数名称 - parameterName
• 所在的类 - containingClass

三、Spring 4.0泛型优化实现

1、ResolvableType

核心 API - org.springframework.core.ResolvableType
• 起始版本:[4.0 , )
• 扮演角色:GenericTypeResolver 和 GenericCollectionTypeResolver 替代者
• 工厂方法:for* 方法
• 转换方法:as* 方法
• 处理方法:resolve* 方法

源码分析

ResolvableType封装Java类型,提供对超类型、接口和泛型参数的访问,以及最终解析为类的能力。

Doc中有一个实例:

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

代码实例

public class ResolvableTypeDemo {

    public static void main(String[] args) {
        // 工厂创建
        // StringList <- ArrayList <- AbstractList <- List <- Collection
        ResolvableType resolvableType = ResolvableType.forClass(StringList.class);

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

        System.out.println(resolvableType.asCollection().resolve()); // 获取 Raw Type(interface java.util.Collection)
        System.out.println(resolvableType.asCollection().resolveGeneric(0)); // 获取泛型参数类型(class java.lang.String)


    }
}

class StringList extends ArrayList<String> {
}

2、ResolvableType 的局限性

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

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

3、ResolvableType 的设计优势

简化 Java 5 Type API 开发,屏蔽复杂 API 的运用,如 ParameterizedType

不变性设计(Immutability)

Fluent API 设计(Builder 模式),链式(流式)编程

参考资料

极客时间-《小马哥讲 Spring 核心编程思想》

相关文章:

  • String类——Java中常见的类(模拟登录案例练习)
  • 从C语言的使用转换到C++(下篇)——刷题、竞赛篇
  • 【C++修行之路】C++入门之深剖变量
  • 【自然语言处理】情感分析(五):基于 BERT 实现
  • C++STL入门:string的基本使用小笔记
  • 【CANoe示例分析】EthernetCanGW_Test_CN
  • 下班前几分钟,我彻底弄懂了并查集
  • 【GIS前沿】什么是新型基础测绘、内容、产品体系、特征?
  • linux性能优化-进程状态
  • 多线程之CAS
  • 【C++提高编程1】一文带你吃透函数模板和类模板(附测试用例源码、测试结果图及注释)
  • 正点原子STM32(基于HAL库)4
  • 【微服务】Docker容器化
  • 一名普通22届本科毕业生|前端程序员|22年年终总结
  • Elasticsearch 需要了解的都在这
  • [笔记] php常见简单功能及函数
  • 77. Combinations
  • Android Volley源码解析
  • PAT A1120
  • Python进阶细节
  • 工作手记之html2canvas使用概述
  • 简单易用的leetcode开发测试工具(npm)
  • 聊聊directory traversal attack
  • 走向全栈之MongoDB的使用
  • Spring第一个helloWorld
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (3)llvm ir转换过程
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (强烈推荐)移动端音视频从零到上手(上)
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (一)基于IDEA的JAVA基础12
  • (转)一些感悟
  • (转载)CentOS查看系统信息|CentOS查看命令
  • ..回顾17,展望18
  • .gitignore文件_Git:.gitignore
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .NET 分布式技术比较
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET/C# 使用反射注册事件
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • [ vulhub漏洞复现篇 ] Apache APISIX 默认密钥漏洞 CVE-2020-13945
  • [Angular 基础] - 自定义指令,深入学习 directive
  • [AS3]URLLoader+URLRequest+JPGEncoder实现BitmapData图片数据保存
  • [BZOJ2850]巧克力王国
  • [BZOJ4337][BJOI2015]树的同构(树的最小表示法)
  • [C# WPF] DataGrid选中行或选中单元格的背景和字体颜色修改
  • [CISCN2019 华北赛区 Day1 Web2]ikun
  • [CISCN2019 华东南赛区]Web4