当前位置: 首页 > 知识库问答 >
问题:

类型擦除在Java映射类中不起作用

赵嘉悦
2023-03-14

我使用javap反编译了Map类。类定义仍然显示泛型类型K和V的存在。这应该被类型擦除的概念擦除。为什么没有发生这种情况?

<代码>/javap-详细java。util。地图

Classfile jar:file:/opt/jdk1.8.0_101/jre/lib/rt.jar!/java/util/Map.class
    Last modified 22 Jun, 2016; size 4127 bytes
    MD5 checksum 238f89b3e2ff9bebe07aa22b0a3493a9
    Compiled from "Map.java"

public interface java.util.Map<K extends java.lang.Object, V extends java.lang.Object>
    minor version: 0
    major version: 52
    flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT

Constant pool:

共有2个答案

屠君墨
2023-03-14

字节码中有额外的信息,用于解码通用信息。

蓟安歌
2023-03-14

如果泛型签名信息被完全擦除,则不可能使用泛型类型或方法,除非您也有源代码。想想看:为了有效地使用泛型,编译器必须知道类型或方法是泛型的,并且它必须知道泛型参数的数量、位置和边界。

为此,javac在类型和方法上发出所谓的签名属性,这些类型和方法本身是泛型的,或者其签名包含类型变量或其他泛型类型的实例化。

对于泛型类型,如映射

  1. 由类型声明的所有泛型参数(类型变量)及其边界
  2. 类型基类的完整泛型签名
  3. 类型实现的接口的完整通用签名

对于映射界面,签名值如下所示:

<K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;

您可以在输出的最后一行的javap-v中看到这个属性,该行位于结束符之后。要了解更完整的泛型签名是什么样子,请查看HashMap类,该类具有泛型基类并实现了多个接口:

<K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable

从这个签名中,编译器知道以下关于类型HashMap的信息:

  1. 有两个通用参数,KV,它们都扩展了java.lang.Object
  2. 基类是java.util.AbstractMap

方法也可能具有Signature属性,但在方法的情况下,签名描述了:

  1. 方法声明的所有泛型参数(类型变量)及其界限

然而,方法的签名被认为是额外的元数据;您永远不会看到字节码中直接引用的。相反,您将看到对方法描述符的引用,它类似于递归应用泛型擦除的签名。与签名属性不同,方法描述符是必需的<代码>javap-v非常好地向你们展示了这两个版本。例如,给定HashMap方法public V put(K,V)

  1. 方法描述符是(Ljava/lang/Object; Ljava/lang/Object;)Ljava/lang/Object;
  2. 通用签名(TK; TV;)TV;

Signature告诉编译器和您的IDE方法的完整通用签名,从而实现类型安全的实施。描述符是如何在调用站点的字节码中实际引用该方法的。例如,给定表达式map.put(0,"零"),其中mapMap

aload            (some variable holding a Map)
iconst_0
invokestatic     java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
ldc              "zero"
invokeinterface  java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

请注意,没有保留任何通用信息。通过插入执行运行时强制转换的checkcast指令,在运行时强制执行有限类型安全。例如,调用map。在地图上获取(0)

aload            (some variable holding a Map)
iconst_0
invokestatic     java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
invokeinterface  java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
checkcast        Ljava/lang/String;

因此,即使Map类型在调用站点被完全擦除,发出的字节码也可以确保从Map检索到的任何值

需要强调的是,与类文件中的大多数元数据一样,签名属性是完全可选的。虽然javac在必要时会发出它们,但它们有可能被字节码优化器和模糊器等后处理器去除。当然,这将使得不可能以预期的方式使用泛型。例如,如果要去掉java/util/Map中的签名属性。类,您只能将Map作为等价于Map的非泛型类来使用

 类似资料:
  • 问题内容: 类型擦除应该擦除所有泛型信息…如果是这种情况,那么像GSON这样的库如何使用泛型来确定反序列化为哪种类型? 例如 这将反序列 化为 将反序列化为 因此以某种方式在运行时使用通用信息。 问题答案: 类型擦除不会擦除所有类型信息。它不会从类,字段,返回类型和参数定义中将其删除。保留以下示例中的类型信息: 这是可以通过反射实现的。您可以检查给定的是否是该类,将其强制转换为该类并获取类型信息。

  • 问题内容: 我以为Java擦除会在编译时消除泛型类型,但是当我自己对其进行测试时,我意识到在Bytecode中有一些有关泛型类型的信息。 这是我的测试: 我写了2节课: 和 我编译了两个类,并在通用类的某个地方看到了这一行 在非泛型类中: 所以很明显我在字节码中有通用信息,那么这个擦除的东西是什么? 问题答案: 一些通用类型信息存储在属性中。请参阅JLS 4.8 和4.6以及JVM规范4.3.4。

  • 问题内容: 有没有一种方法可以避免类型擦除并获得对类型参数的访问? 我需要了解并使用它来做事。是否可以,如果可以,如何在 不 将类传入构造函数或参数之外的任何地方的 情况下 完成操作? 编辑: 这个问题的主要目的是找出是否有 任何实用的方法 来消除类型擦除。 问题答案: 实际上, 没有 类型擦除的实用方法,因为您不能要求运行时无法访问的内容。当然,假设您同意为实现接口的每个枚举子类化通用类是一个实

  • 我有一个这样定义的方法: 我的单元测试我错误地这样测试它: 这应该会产生编译时错误(当我尝试使用javac编译它时,它会产生编译时错误),但eclipse允许它编译。 我真的不明白为什么eclipse允许这段代码编译,有人能提供一些信息吗,为什么这会在eclipse中编译? eclipse版本是:面向Web开发人员的eclipse Java EE IDE。版本:Luna Service Relea

  • OracleDoc说泛型是在java中使用调用类型擦除的技术实现的,这就是它的工作原理。 如果类型参数是无界的,则用其边界或对象替换泛型类型中的所有类型参数。因此,生成的字节码只包含普通类、接口和方法 所以,如果我有一个通用类说容器如下: 通过类型擦除处理后,它的等效类将如下所示: 如果这里有错误,请纠正我 类似地,如果a修改上述类,如下所示 这不会相当于??? 如果这是真的,那么我也应该有这样的

  • 我在目标语言为python的swig接口文件中成功地使用了以下类型映射: 然而,当我将目标语言更改为java时,当我尝试构建swg生成的自动生成的c包装代码时,我会遇到很多编译时错误,即。 等等... 我注意到,在python案例中,粘贴到swig包装器代码中的代码包含,等声明,这些代码来自 它位于 当目标语言为java时,此等效代码不会粘贴到swig包装器代码中。我不完全确定为什么不会发生这种情