首页 > 编程语言 >JAVA泛型擦除

JAVA泛型擦除

时间:2023-04-18 13:35:49浏览次数:40  
标签:... JAVA sun tree 类型 擦除 泛型

目录

Java泛型

深入理解Java虚拟机 第三版 10.3.1

泛型的本质是参数化类型(Parameterized Type)或者参数化多态(Parametric Polymorphism)的应用,即可以将操作的数据类型指定为方法签名中的一种特殊参数,这种参数类型能够用在类、接口和方法的创建中,分别构成泛型类、泛型接口和泛型方法。泛型让程序员能够针对泛化的数据类型编写相同的算法,这极大地增强了编程语言的类型系统及抽象能力。

Java选择的泛型实现方式叫作“类型擦除式泛型”(Type Erasure Generics)

...

Java语言中的泛型则不同,它只在程序源码中存在,在编译后的字节码文件中,全部泛型都被替换为原来的裸类型了,并且在相应的地方插入了强制转型代码

...

直观看擦除

泛型擦除前:

image-20220613124257932

编译后:

image-20220613124447715

当前看来是没有被擦除的,这是因为在反编译是,工具将泛型信息也同步进行了反编译,通过字节码是可以看泛型是被擦除了

image-20220613124708604

后面源码阶段也可以看单擦除的过程

泛型实现的相关定义

树节点

JCTypeParameter(类型参数树节点):

    来源:com.sun.tools.javac.tree.JCTree.JCTypeParameter
    // 类型参数对象,泛型
    // 每个形式类型参数都是一个JCTypeParameter对象
    // 可以表示类型(接口或类)或者方法声明的类型参数
    public static class JCTypeParameter extends JCTree implements TypeParameterTree {
        // 保存类型参数中类型变量的名称
        public Name name;
        // bounds保存类型变量的上界,可以有多个(泛型)
        public List<JCExpression> bounds;
				...
    }

JCWildcard(通配符树节点):

    来源:com.sun.tools.javac.tree.JCTree.JCWildcard
    // 通配符类型
    public static class JCWildcard extends JCExpression implements WildcardTree {
        // 保存通配符的类型
        public TypeBoundKind kind;
        // 通配符类型上界或下界
        public JCTree inner;
				...
    }
		来源:com.sun.tools.javac.tree.JCTree.TypeBoundKind
    // 通配符的类型
    public static class TypeBoundKind extends JCTree {
        // 通配符的类型
        public BoundKind kind;
    }
来源:com.sun.tools.javac.code.BoundKind
public enum BoundKind {
    // 下界通配符
    EXTENDS("? extends "),
    // 上界通配符
    SUPER("? super "),
    // 无界通配符
    UNBOUND("?");

    private final String name;
}

类型

ForAll(含有泛型变量声明的方法类型,主要辅助进行类型推断)

    来源:com.sun.tools.javac.code.Type.ForAll
    public static class ForAll extends DelegatedType implements ExecutableType {
        // 具体的类型变量
        public List<Type> tvars;
        ...
		}

泛型相关抽象语法树的生成

已下面代码为例,生成抽象语法树:

package book.chapter13;

import java.io.Serializable;

/*
泛型擦除前
 */
class Test10<X extends Integer, Y extends Serializable & Cloneable, Z extends X> {
    X x;
    Y y;
    Z z;
}

抽象语法树生成

来源:com.sun.tools.javac.parser.JavacParser#parseCompilationUnit
    public JCTree.JCCompilationUnit parseCompilationUnit() {
        ...
        // 出现结束符跳出循环
        while (S.token() != EOF) {
           ...
                // 解析类声明
                JCTree def = typeDeclaration(mods);
                defs.append(def);
           ...
        }
        JCTree.JCCompilationUnit toplevel = F.at(pos).TopLevel(packageAnnotations, pid, defs.toList());
        ...
        return toplevel;
    }

解析类、接口、枚举的定义

来源:com.sun.tools.javac.parser.JavacParser#classOrInterfaceOrEnumDeclaration
      JCStatement classOrInterfaceOrEnumDeclaration(JCModifiers mods, String dc) {
        // 解析类
        if (S.token() == CLASS) {
            return classDeclaration(mods, dc);
            // 解析接口
        } else if (S.token() == INTERFACE) {
            return interfaceDeclaration(mods, dc);
            // 解析枚举类
        } else if (allowEnums) {
            if (S.token() == ENUM) {
                return enumDeclaration(mods, dc);
            } else {
                ...
            }
        } else {
           ... 
        }
    }

类声明解析

来源:com.sun.tools.javac.parser.JavacParser#classDeclaration
    JCClassDecl classDeclaration(JCModifiers mods, String dc) {
        ...
        // 解析类型参数
        List<JCTypeParameter> typarams = typeParametersOpt();
				...
        // 创建类声明对象,并返回
        JCClassDecl result = toP(F.at(pos).ClassDef(
            mods, name, typarams, extending, implementing, defs));
        return result;
    }

参数化类型解析

来源:com.sun.tools.javac.parser.JavacParser#typeParametersOpt
    List<JCTypeParameter> typeParametersOpt() {
  			// LT : '<'
        if (S.token() == LT) {
            ...
            ListBuffer<JCTypeParameter> typarams = new ListBuffer<JCTypeParameter>();
          	// 跳转到下一个token
            S.nextToken();
          	// 解析类型参数
            typarams.append(typeParameter());
            // 循环对多个类型参数进行解析
            while (S.token() == COMMA) {
              	// 跳转到下一个token
                S.nextToken();
              	// 解析类型参数
                typarams.append(typeParameter());
            }
          	// GT : '>'
            accept(GT);
            return typarams.toList();
        } else {
            return List.nil();
        }
    }
来源:com.sun.tools.javac.parser.JavacParser#typeParameter
      JCTypeParameter typeParameter() {
        ...
        // 类型参数的name  
        Name name = ident();
  			// 当前类型参数的上界列表,可以有多个上界
        ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>();
        // 类型参数中声明上界,EXTENDS:'extends',没有默认上界为Object
        if (S.token() == EXTENDS) {
          	// 切换到下一个token
          	S.nextToken();
          	// 添加到上界列表
            bounds.append(parseType());
         		// AMP:'&',可以有多个上界,用&符间隔
            while (S.token() == AMP) {
                S.nextToken();
              	// 添加到上界列表
                bounds.append(parseType());
            }
        }
  			// 组装并返回当前类型参数的抽象树节点
        return toP(F.at(pos).TypeParameter(name, bounds.toList()));
    }
来源:com.sun.tools.javac.parser.JavacParser#parseType
      public JCExpression parseType() {
  			// 解析赋值表达式,TYPE表示当前赋值表达式返回期望为一个类型
        return term(TYPE);
    }

抽象语法树

抽象语法树

泛型擦除

Java的泛型本质上是语法糖的一种,所以在解语法糖中对泛型信息进行擦除

来源:com.sun.tools.javac.main.JavaCompiler#desugar
	protected void desugar() {
      // 泛型擦除,擦除后返回新的树节点
      env.tree = transTypes.translateTopLevelClass(env.tree, localMake);
	}


调用translateTopLevelClass()方法会调用TransTypes类的translate()方法。TransTypes类中有一系列重载的translate()方法,通过translate()方法遍历整个语法树。

泛型擦除前的树节点

image-20220613151630665

类的泛型擦除

来源:com.sun.tools.javac.comp.TransTypes#translateClass
void translateClass(ClassSymbol c) {
        ...
            try {
              	// 类型定义
                JCClassDecl tree = (JCClassDecl) env.tree;
              	// 类型参数列表置为空
                tree.typarams = List.nil();
              	// 访问class树节点的内容
                super.visitClassDef(tree);
                ...
                // 树节点类型进行擦除
                tree.type = erasure(tree.type);
            } finally {
               ...
            }
  			...
    }

泛型擦除抽象语法树

image-20220613152114362

变量的泛型擦除

来源:com.sun.tools.javac.tree.TreeTranslator#visitClassDef
    public void visitClassDef(JCClassDecl tree) {
        tree.mods = translate(tree.mods);
        tree.typarams = translateTypeParams(tree.typarams);
        tree.extending = translate(tree.extending);
        tree.implementing = translate(tree.implementing);
  			// 翻译class节点定义子节点
        tree.defs = translate(tree.defs);
        result = tree;
    }
来源:com.sun.tools.javac.comp.TransTypes#visitVarDef
    public void visitVarDef(JCVariableDecl tree) {
  			// 变量类型翻译
        tree.vartype = translate(tree.vartype, null);
  			// 初始值翻译
        tree.init = translate(tree.init, tree.sym.erasure(types));
        // 类型擦除
        tree.type = erasure(tree.type);
        result = tree;
    }
来源:com.sun.tools.javac.code.Types#erasure(com.sun.tools.javac.code.Type, boolean)
// 对泛型擦除的方法都会间接调用到
      private Type erasure(Type t, boolean recurse) {
        if (t.tag <= lastBaseTag)
            return t; /* fast special case */
        else
          	// erasure.visit返回值解释擦除后的类型
            return erasure.visit(t, recurse);
        }

泛型擦除后的抽象语法树

image-20220613154104691

类型推断

标签:...,JAVA,sun,tree,类型,擦除,泛型
From: https://www.cnblogs.com/ylc0x01/p/17329224.html

相关文章

  • JavaScript 内的 this 指向
     在javascript语言中,有一个奇奇怪怪的"关键字"叫做this● 为什么说它是奇奇怪怪呢,是因为你写出100个this,可能有100个解释,完全不挨边● 但是,在你的学习过程中,搞清楚了this这个玩意,那么会对你的开发生涯有很大帮助的● 接下来咱们就开始一点一点......
  • JAVAC编译流程
    目录Javac编译过程入口java源代码词法分析->token流语法分析->抽象语法树语义分析->标注语法树AttrFlowLower代码生成->字节码Javac编译过程入口来源:com.sun.tools.javac.main.Main#compile(java.lang.String[])publicintcompile(String[]args){//创......
  • 论 java.net.SocketException: sendto failed: EPIPE (Broken pipe) 的解决办法
    这里只是针对我昨天遇到的问题(上传文件过大,导致出现Socket异常)的解决办法。众所周知,tomcat是有默认的文件传输大小限制的(跟android前端),后来跟服务器的哥们协调了一下,他那边改成多少都不管用,这是其一。其二是后来查google得知,tomcat设置服务器的超时时间,后来还是一样,设置成多少都不......
  • [Maven] The JAVA_HOME environment variable is not definded...... 配置错误日志
    ...因为是第一次配置Maven,所以出现了很多错误所以列举一二。出现找不到Java_Home环境变量的时候,如图。这个时候可能是,你的Java_Home路径配置错误,在Java_Home路径中,将\bin删掉,在path后面跟\bin,如%java_home%\bin,而不是直接在java_home中直接写死。......
  • java 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接
    通过java程序调用sqlserver数据库,报错驱动程序无法通过使用安全套接字层(SSL)加密与SQLServer建立安全连接。错误:“TheserverselectedprotocolversionTLS10isnotacceptedbyclientpreferences[TLS12]”。ClientConnectionId:9c0c766b-97d5-41c6-884e-1ecbdefbac0......
  • Java实现延迟执行代码
    Java实现延迟执行代码对于Java程序在它们的操作中添加延迟或暂停是比较常见的。这对于任务暂停直到另外任务完成执行场景比较有用。本文我们提供两类方法实现延迟执行。1.基于线程(Thread)方法当Java查询运行时,它在操作系统上产生一个进程,其至少包含一个线程————主线程负责......
  • java参数值传递
    题目: (1)定义一个Circle类,包含一个double型的radius属性代表圆的半径 *一个findAre()方法返回圆的面积。 *  *(2)定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下: *  *publicvoidprintAreas(Circlec,inttime) *在printAreas方法中......
  • java 递归方法 计算1-100之间的所有自然数的和 计算1-100之间所
    packageprectice;/***递归方法的使用**递归方法的定义:一个方法体内调用他自身**①方法递归包含了一种隐式循环,它会重复执行某段代码,但这种重发执行无须循环控制。*②递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似死循环。** 例1:计......
  • java笔试题目——要求:仅打印出a=100,b=200,请写出method方法的代码
    //题目:publicclassTest{publicstaticvoidmain(String[]args){inta=10;intb=10;method(a,b);//需要在method方法被调用之后,仅打印出a=100,b=200,请写出method方法的代码。System.out.println("a="+a);S......
  • Java 实现Arrays 数组工具类
    ClassArrays是java工具包自带的非常强大的数组工具类,今天手工实现了一部分功能,部分参考实现如下publicclassMyArrays{//最大值/***获取int数组最大值**@paramarr:代遍历的数组*@return数组最大值*/publicintgetMax(......