文档章节

泛型擦除

天王盖地虎626
 天王盖地虎626
发布于 06/21 18:58
字数 1193
阅读 3
收藏 0

擦除的现象

当开始深入研究泛型的时,会发现其实有些东西是没有意义的。例如,我们可以声明ArrayList.class,但是却无法声明ArrayList<Integer>.class
这是因为泛型的擦除机制造成的,考虑以下的情况。

public class ErasedTypeEquivalence {
    public static void main(String[] args) {
        Class c1 = new ArrayList<String>().getClass();
        Class c2 = new ArrayList<Integer>().getClass();
        System.out.println("(c1 == c2) = " + (c1 == c2));
    }
}

以上代码中,表明ArrayList<String>ArrayList<Integer>是同一类型。不同的类型在行为方面肯定不同。例如,如果试着将一个Integer类型放入ArrayList<String>,所得的行为和Integer类型放入ArrayList<Integer>完全不同,但是它们仍然是同一类型。
以下的代码是对这个问题的一个补充。

class Frob {
}

class Fnorkle {
}

class Quark<Q> {
}

class Particle<POSITION, MOMENTUM> {
}

public class LostInfomation {
    public static void main(String[] args) {
        List<Frob> list = new ArrayList<>();
        Map<Frob,Fnorkle> map = new HashMap<>();
        Quark<Fnorkle> quark = new Quark<>();
        Particle<Long,Double> p = new Particle<>();
        System.out.println(Arrays.toString(
                list.getClass().getTypeParameters()
        ));
        System.out.println(Arrays.toString(
                map.getClass().getTypeParameters()
        ));
        System.out.println(Arrays.toString(
                quark.getClass().getTypeParameters()
        ));
        System.out.println(Arrays.toString(
                p.getClass().getTypeParameters()
        ));
    }
}
// Outputs
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]

Class.getTypeParameters是返回一个TypeVariable对象数组,表示泛型声明所声明的类型参数。但是上例的输出也表明了,这个方法获得的只是做参数占位符的标识符。

擦除的概念

在泛型代码内部,无法获得任何有关泛型参数类型的信息。
Java的泛型是使用擦除来实现的,这就意味着在使用泛型的时候,任何具体的类型信息都会被擦除。写代码时唯一知道就是在使用一个对象。因此,List<String>List<Integer>在运行时事实上是相同的类型。这两种形式都会被擦除成它们的"原生"类型,即List。理解擦除以及应该如何处理它,是在学习Java泛型时候的最大阻碍。

因此,可以获得类型参数标识符和泛型类型边界这样的信息,但是却无法知道用来创建某个特定实例的实际的类型参数。

擦除的边界

class HasF{
    public void f(){
        System.out.println("HasF.f()");
    }
}

class Manipulator<T> {
    private T obj;

    public Manipulator(T obj) {
        this.obj = obj;
    }

    public void manipulate(){
        //obj.f() compile error
    }
}

public class Manipulation {
    public static void main(String[] args) {
        HasF hf = new HasF();
        Manipulator<HasF> manipulator = new Manipulator<>(hf);
        manipulator.manipulate();
    }
}

由于有了擦除机制,Java编译器无法将manipulate()必须能够在obj上调用f()这一需求映射到HasF拥有f()这一事实上,为了调用f(),我们必须协助泛型类,给定泛型类的边界,以便告知编译器只能遵循这个边界的类型。这里重用了extends关键字。并由于有了边界,下面的代码可以编译了。

class Manipulator2<T extends HasF> {
    private T obj;

    public Manipulator2(T obj) {
        this.obj = obj;
    }

    public void manipulate(){
        obj.f();
    }
}

上面的代码中,边界<T extentds HasF>声明T必须具有类型HasF或者从HasF导出来的类型,因为这个约束,所以可以安全地在obj上调用f了。
这里说泛型的类型参数将擦除到它的第一边界(泛型可能有多个边界)。这里提到了类型参数的擦除,编译器实际上会把类型参数替换成它的擦除,就像上面的示例那样,T擦除到了HasF,就像在类的声明中用HasF替换成T一样。
如同上文所说,我们可以不使用泛型,直接将T替换回会HasF

class Manipulator3 {
    private HasF obj;

    public Manipulator2(HasF obj) {
        this.obj = obj;
    }

    public void manipulate(){
        obj.f();
    }
}

上面的代码也可以像Manipulator2中那样正常工作。但是这并不意味着带边界的泛型是毫无意义的。
只有当希望使用的类型参数比某个具体类型(以及它的所有子类型)更加"泛化"时。也就是说,当希望代码能跨多个类工作的时候,使用泛型才有帮助。因此,类型参数和它们在有用的泛型代码中的应用,通常比简单的类替换要更为复杂。。但是也不能因为觉得<T extends HasF>的任何东西都是有缺陷的。
例如,假设某个类有返回T的方法,那么泛型在这里就是有用处的,因为泛型可以返回确切的类型。例子如下。

class ReturnGenericType<T extends HasF> {
    private T obj;

    public ReturnGenericType(T obj) {
        this.obj = obj;
    }

    public T getObj() {
        return obj;
    }
}

所以,必须查看所有的代码。并确定它是否"足够复杂"到必须使用泛型的程度。

本文转载自:https://www.jianshu.com/p/eb315a331b8c

天王盖地虎626

天王盖地虎626

粉丝 32
博文 527
码字总数 20708
作品 0
南京
私信 提问
泛型(二)->擦除&擦除带来的问题

泛型(二)->擦除&擦除带来的问题 本篇首先介绍泛型的擦除,然后围绕泛型擦除所带来的问题进行精确打击,话不多说,我们直接开始正文. 文中很多例子都会用到Pair这个对象,这里统一声明. 泛型擦除 ...

zly921112
2017/03/19
0
0
Java编程思想: 泛型

简单泛型 Java SE5之前是使用Object来实现其泛型的: 但这种实现方式存在两个的问题: 1. 由于存储的是Object类型, 则实际调用时候需要强制转换回原类型. 如执行t.get()操作时候, 实际上将对象...

fzyz_sb
2016/07/26
95
0
Java泛型--擦除

Java泛型--目录 概述 我们可以声明ArrayList.class,但是不能声明ArrayList<Integer>.class,看下面的例子: public class EqualClassName { public static void main(String[] args) { //声明不......

我叫张凯
2016/01/12
1K
0
Java泛型-类型擦除

一、概述 Java泛型在使用过程有诸多的问题,如不存在List<String>.class, List<Integer>不能赋值给List<Number>(不可协变),奇怪的ClassCastException等。 正确的使用Java泛型需要深入的了...

lwwjing
2016/03/17
73
0
泛型的内部原理:类型擦除以及类型擦除带来的问题

参考:java核心技术 一、Java泛型的实现方法:类型擦除 Java的泛型是伪泛型。为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉。正确理解泛型概念的首要前提是理...

小菜鸡1
2016/07/27
489
1

没有更多内容

加载失败,请刷新页面

加载更多

springboot2.0 maven打包分离lib,resources

springboot将工程打包成jar包后,会出现获取classpath下的文件出现测试环境正常而生产环境文件找不到的问题,这是因为 1、在调试过程中,文件是真实存在于磁盘的某个目录。此时通过获取文件路...

陈俊凯
今天
5
0
BootStrap

一、BootStrap 简洁、直观、强悍的前端开发框架,让web开发更加迅速、简单 中文镜像网站:http://www.bootcss.com 用于开发响应式布局、移动设备优先的WEB项目 1、使用boot 创建文件夹,在文...

wytao1995
今天
9
0
小知识:讲述Linux命令别名与资源文件的区别

别名 别名是命令的快捷方式。为那些需要经常执行,但需要很长时间输入的长命令创建快捷方式很有用。语法是: alias ppp='ping www.baidu.com' 它们并不总是用来缩短长命令。重要的是,你将它...

老孟的Linux私房菜
今天
8
0
《JAVA核心知识》学习笔记(6. Spring 原理)-5

它是一个全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层。但是 Spring 仍然可以和其他的框架无缝整合。 6.1.1. Spring 特点 6.1.1.1. 轻量级 6.1.1.2. 控制反转 6.1.1....

Shingfi
今天
7
0
Excel导入数据库数据+Excel导入网页数据【实时追踪】

1.Excel导入数据库数据:数据选项卡------>导入数据 2.Excel导入网页数据【实时追踪】:

东方墨天
今天
10
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部