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

目录
  • 前言
    • 泛型的类型擦除原则是:
  • 1 擦除类定义中的类型参数
    • 1.1 无限制类型擦除
    • 1.2 有限制类型擦除
  • 2 擦除方法定义中的类型参数
  • 3 桥接方法和泛型的多态
  • 总结
  • 参考资料

前言

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

泛型的类型擦除原则是:

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

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

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

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

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

1.1 无限制类型擦除

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

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

1.2 有限制类型擦除

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

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

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

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

图 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的相关章节。

总结

到此这篇关于Java泛型类型擦除的文章就介绍到这了,更多相关Java泛型类型擦除内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

参考资料

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

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

(0)

相关推荐

  • JAVA泛型的继承和实现、擦除原理解析

    这篇文章主要介绍了JAVA泛型的继承和实现.擦除原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 很多的基础类设计会采用泛型模式,有些应用在使用的时候处于隔离考虑,会进行继承,此时子类如何继承泛型类就很讲究了,有些情况下需要类型擦除,有些情况下不需要类型擦除,但是大多数情况下,我们需要的是保留父类的泛型特性.因为类型被擦除后,得到的对象会是Object,此时会导致编码或反序列化失败. 如下所示: 如果子类也继承父类的泛型化,则类型不会被擦

  • 详解java 中泛型中的类型擦除和桥方法

    在Java中,泛型的引入是为了在编译时提供强类型检查和支持泛型编程.为了实现泛型,Java编译器应用类型擦除实现: 1.  用类型参数(type parameters)的限定(如果没有就用Object)替换泛型类型中的所有类型参数. 2.  需要保持类型安全的时候插入类型转换(隐含插入) 3.  在extened 泛型类型中生成桥方法来保证多态性 类型擦除确保不会为已参数化了的类型(paramterized types)产生新类,这样泛型能保证没有运行时的负载. 泛型类型擦除 在类型擦除过程中,

  • 简单理解java泛型的本质(非类型擦除)

    背景 之前在网上发现这个问题 public class GenericTest { //方法一 public static <T extends Comparable<T>> List<T> sort(List<T> list) { return Arrays.asList(list.toArray((T[]) new Comparable[list.size()])); } //方法二 public static <T extends Compara

  • Java 泛型总结(一):基本用法与类型擦除

    简介 Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型.泛型可以用于类.接口.方法,通过使用泛型可以使代码更简单.安全.然而 Java 中的泛型使用了类型擦除,所以只是伪泛型.这篇文章对泛型的使用以及存在的问题做个总结,主要参考自 <Java 编程思想>. 这个系列的另外两篇文章: Java 泛型总结(二):泛型与数组 Java 泛型总结(三):通配符的使用 基本用法 泛型类 如果有一个类 Holder 用于包装一个变量,这个变

  • Java编程探索之泛型擦除实例解析

    1.问题引出 源码: public static void main(String[] args) { List<Integer> a = new ArrayList<Integer>(); List<String> b = new ArrayList<String>(); System.out.println(a.getClass() == b.getClass());//结果true } 编译后L public static void main(Stri

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

    目录 前言 泛型的类型擦除原则是: 1 擦除类定义中的类型参数 1.1 无限制类型擦除 1.2 有限制类型擦除 2 擦除方法定义中的类型参数 3 桥接方法和泛型的多态 总结 参考资料 前言 Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了"伪泛型"的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的"类型擦除"(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生

  • Java泛型之类型擦除实例详解

    目录 前言 泛型是什么? 泛型的定义和使用 泛型类 泛型方法 泛型类与泛型方法的共存现象 泛型接口 通配符 ? 无限定通配符 <?> <? extends T> 类型擦除 类型擦除带来的局限性 泛型中值得注意的地方 Java 不能创建具体类型的泛型数组 泛型,并不神奇 总结 前言 泛型,一个孤独的守门者. 大家可能会有疑问,我为什么叫做泛型是一个守门者.这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇.泛型是 Java 中一个很小巧的概念,但同时

  • 详解Java泛型中类型擦除问题的解决方法

    以前就了解过Java泛型的实现是不完整的,最近在做一些代码重构的时候遇到一些Java泛型类型擦除的问题,简单的来说,Java泛型中所指定的类型在编译时会将其去除,因此List 和 List 在编译成字节码的时候实际上是一样的.因此java泛型只能做到编译期检查的功能,运行期间就不能保证类型安全.我最近遇到的一个问题如下: 假设有两个bean类 /** Test. */ @Data @NoArgsConstructor @AllArgsConstructor public static class

  • java 用泛型参数类型构造数组详解及实例

    java 用泛型参数类型构造数组详解及实例 前言: 前一阵子打代码的时候突然想到一个问题.平时我们的数组都是作为一个参数传入方法中的,如果我们要想在方法中创建一个数组怎么样呢?在类型明确的情况下,这是没什么难度的.如果我们传入的参数是泛型类型的参数呢? public static <T> T[] creArray (T obj){ T[] arr = new T[10]; } 像上面这种用T来直接new数组的方法是错误的,会编译时出现一个:Cannot create a generic arr

  • Java中的反射机制示例详解

    目录 反射 什么是Class类 获取Class实例的三种方式 通过反射创建类对象 通过反射获取类属性.方法.构造器 更改访问权限和实例赋值 运用场景 反射 反射就是把Java类中的各个成分映射成一个个的Java对象.即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法:对于任意一个对象,都能调用它的任意一个方法和属性.这种动态获取信息及动态调用对象方法的功能叫Java的反射机制 每一个Java程序执行必须通过编译.加载.链接和初始化四个阶段 1.编译:将.java.文件编译成字节码.

  • Java基本语法之内部类示例详解

    目录 1.内部类概念及分类 2.实例内部类 2.1实例内部类的创建 2.2使用.this和.new 2.3内部类实现迭代打印 2.4内部类的继承 3.静态内部类 4.匿名内部类 1.内部类概念及分类 将一个类定义在另一个类的内部或者接口内部或者方法体内部,这个类就被称为内部类,我们不妨将内部类所在的类称为外围类,除了定义在类,接口,方法中的内部类,还有一种特殊的内部类,那就是使用关键字new创建一个匿名类的对象,而这个匿名类其实就是一个内部类,具体说是一个匿名内部类,经常用于传入构造器实参构造对

  • Java设计模式之策略模式示例详解

    目录 定义 结构 UML类图 UML序列图 深入理解策略模式 策略和上下文的关系 策略模式在JDK中的应用 该策略接口有四个实现类 策略模式的优点 策略模式的缺点 策略模式的本质 在讲策略模式之前,我们先看一个日常生活中的小例子: 现实生活中我们到商场买东西的时候,卖场往往根据不同的客户制定不同的报价策略,比如针对新客户不打折扣,针对老客户打9折,针对VIP客户打8折... 现在我们要做一个报价管理的模块,简要点就是要针对不同的客户,提供不同的折扣报价. 如果是有你来做,你会怎么做? 我们很有可

  • Java垃圾回收机制的示例详解

    目录 一.概述 二.对象已死? 1.引用计数算法 2.可达性分析算法 3.四种引用 4.生存还是死亡? 5.回收方法区 三.垃圾收集算法 1.分代收集理论 2.名词解释 3.标记-清除算法 4.标记-复制算法 5.标记-整理算法 一.概述 说起垃圾收集(Garbage Collection,下文简称GC),有不少人把这项技术当作Java语言的伴生产 物.事实上,垃圾收集的历史远远比Java久远,在1960年诞生于麻省理工学院的Lisp是第一门开始使 用内存动态分配和垃圾收集技术的语言.当Lisp

  • Java设计模式之原型设计示例详解

    目录 简单说一下(定义) 稍微夸一下(优缺点) 顺便提一下(适用场景) 着重讲一下(深.浅克隆) 多多用一下(结构.代码实现) 简单说一下(定义) 什么是原型模式:原型模式是用于创建重复的对象,同时又能保证性能.用一个已经创建的实例作为原型,通过复制该原型对象来创建一个或者多个和原型相同或者相似的新对象 举例说明:我们都玩过打飞机的游戏,敌军的飞机可谓是数不胜数,但是如果每出一架敌机都要重新实例化的话,那么自然我们的功能很复杂.所以这个时候我们的原型模式就派上用场了,只实例化一架飞机出来,其他的

  • java设计模式策略模式图文示例详解

    目录 策略模式 意图 问题 解决方案 真实世界类比 策略模式结构 伪代码 策略模式适合应用场景 实现方式 策略模式优缺点 策略模式优缺点 与其他模式的关系 策略模式 亦称:Strategy 意图 策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换. 问题 一天,你打算为游客们创建一款导游程序.该程序的核心功能是提供美观的地图,以帮助用户在任何城市中快速定位. 用户期待的程序新功能是自动路线规划:他们希望输入地址后就能在地图上看到前往目的

随机推荐