Java泛型的类型擦除示例详解

作者:宿宝臣 时间:2023-07-02 13:38:17 

前言

Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。理解类型擦除对于用好泛型是很有帮助的,尤其是一些看起来“疑难杂症”的问题,弄明白了类型擦除也就迎刃而解了。

泛型的类型擦除原则是:

•消除类型参数声明,即删除<>及其包围的部分。

•根据类型参数的上下界推断并替换所有的类型参数为原生态类型:如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类)。

•为了保证类型安全,必要时插入强制类型转换代码。

•自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”。

1 擦除类定义中的类型参数

1.1 无限制类型擦除

当类定义中的类型参数没有任何限制时,在类型擦除中直接被替换为Object,即形如<T>和<?>的类型参数都被替换为Object,参见1。

Java泛型的类型擦除示例详解

图 1: 擦除类定义中的类型参数

1.2 有限制类型擦除

当类定义中的类型参数存在限制(上下界)时,在类型擦除中替换为类型参数的上界或者下界,比如形如<T extends Number>和<? extends Number>的类型参数被替换为Number,<? super Number>被替换为Object,参见2。

Java泛型的类型擦除示例详解

图 2: 擦除类定义中的有限制类型参数

2 擦除方法定义中的类型参数

擦除方法定义中的类型参数原则和擦除类定义中的类型参数是一样的,这里仅以擦除方法定义中的有限制类型参数为例,见3。

Java泛型的类型擦除示例详解

图 3: 擦除泛型方法中的类型参数

3 桥接方法和泛型的多态

考虑下面的代码:


public interface Info&lt;T&gt; {
   // just return var:-)
   T info(T var);
}
public class BridgeMethodTest implements Info&lt;Integer&gt; {
   @Override
   public Integer info(Integer var) {
       return var;
   }
}

按照我们之前类型擦除的经验,在擦除类型后的代码应该是这个样子的:


public interface Info {
   // just return var
   Object info(Object var);
}

public class BridgeMethodTest implements Info {
   @Override
   public Integer info(Integer var) {
       return var;
   }
}

但是,明显可以看出,这样擦除类型后的代码在语法上是错误的:BridgeMethodTest类中虽然存在一个info方法,但是和Info接口要求覆盖的info方法不一致:参数类型不一致。在这种情况下,Java编译器会自动增加一个所谓的“桥接方法”(bridge method)来满足Java语法的要求,同时也保证了基于泛型的多态能够有效。我们反编译一下BridgeMethodTest.class文件可以看到Java编译器到底是如何做的:


$ javap BridgeMethodTest.class

Compiled from “BridgeMethodTest.java”

public class BridgeMethodTest implements Info<java.lang.Integer> {

public BridgeMethodTest();

public java.lang.Integer info(java.lang.Integer);

public java.lang.Object info(java.lang.Object);

}

可以看出,Java编译器在BridgeMethodTest中自动增加了两个方法:默认构造方法和参数为Object的info方法,参数为Object的info方法就是“桥接方法”。如何理解“桥接”二字呢?我们进一步反编译BridgeMethodTest看一下:


// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.                                                          
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3)
// Source File Name:   BridgeMethodTest.java

public class BridgeMethodTest
   implements Info
{

public BridgeMethodTest()
   {  
   }  

public Integer info(Integer integer)
   {  
       return integer;
   }  

public volatile Object info(Object obj)
   {  
       return info((Integer)obj);
   }  
}

info(Object)方法通过调用子类的info(Integer)方法搭起了父类和子类的桥梁,也就是说,info(Object obj)这个方法起到了连接父类和子类的作用,使得Java的多态在泛型情况下依然有效。

当然,我们在使用基于泛型的多态时不必过多的考虑“桥接方法”,Java编译器会帮我们打理好一切。

关于桥接方法的更多信息可以参考:JLS的相关章节。

参考资料

•http://docs.oracle.com/javase/tutorial/java/generics/index.html

•http://docs.oracle.com/javase/tutorial/extra/generics/index.html

来源:http://softlab.sdut.edu.cn/blog/subaochen/2017/01/generics-type-erasure/

标签:java,泛型,擦除
0
投稿

猜你喜欢

  • Intelli IDEA安装Scala插件并安装Scala软件和配置环境变量的详细教程

    2023-02-16 23:49:01
  • SSH框架网上商城项目第24战之Struts2中处理多个Model请求的方法

    2023-02-14 20:49:21
  • 解析Java线程同步锁的选择方法

    2023-02-08 11:02:32
  • android 判断网络是否可用与连接的网络是否能上网

    2021-10-08 02:04:34
  • c# 反射+自定义特性保存数据至本地

    2023-03-14 03:07:40
  • spring框架集成flyway项目的详细过程

    2023-09-14 11:47:14
  • Android实现SQLite添加、更新及删除行的方法

    2022-03-29 05:23:45
  • Android自定义控件ViewGroup实现标签云(四)

    2021-11-30 14:58:36
  • Android ContentProvider查看/读取手机联系人实例

    2021-10-13 07:10:13
  • CCF考试试题之门禁系统java解题代码

    2022-10-05 02:39:27
  • Java C++ 算法leetcode828统计子串中唯一字符乘法原理

    2022-05-09 11:19:18
  • Android中的深度链接技术实战

    2023-09-20 11:27:18
  • 详解使用SSM实现简单工作流系统之实现篇

    2021-10-21 07:49:23
  • Android布局技巧之使用ViewStub

    2023-03-22 06:34:10
  • Android Service详解及示例代码

    2021-12-24 05:44:17
  • 聊聊Java的switch为什么不支持long

    2023-08-24 17:35:14
  • Entity Framework主从表数据加载方式

    2022-03-10 21:44:14
  • MyBatis动态sql查询及多参数查询方式

    2023-05-30 12:20:12
  • Spring Security自定义登录原理及实现详解

    2022-11-20 21:57:39
  • 基于JDK动态代理原理解析

    2022-07-24 19:43:33
  • asp之家 软件编程 m.aspxhome.com