Java的泛型大家都知道是类型擦除的方式实现的,“编译器会进行泛型擦除”是一个常识了(实际擦除的是参数和自变量的类型)。“类型擦除” 并非像许多开发者认为的那样,在 <..> 符号内的东西都被擦除了。看下面这段代码:

  

 public class ClassTest {
public static void main(String[] args) throws Exception {
ParameterizedType type = (ParameterizedType)
Bar.class.getGenericSuperclass();
System.out.println(type.getActualTypeArguments()[0]); ParameterizedType fieldType = (ParameterizedType)
Foo.class.getField("children").getGenericType();
System.out.println(fieldType.getActualTypeArguments()[0]); ParameterizedType paramType = (ParameterizedType)
Foo.class.getMethod("foo", List.class)
.getGenericParameterTypes()[0];
System.out.println(paramType.getActualTypeArguments()[0]); System.out.println(Foo.class.getTypeParameters()[0]
.getBounds()[0]);
} class Foo<E extends CharSequence> {
public List<Bar> children = new ArrayList<Bar>();
public List<StringBuilder> foo(List<String> foo) {return null; }
public void bar(List<? extends String> param) {}
} class Bar extends Foo<String> {}
}

你知道输出了什么吗?

 class java.lang.String
class ClassTest$Bar
class java.lang.String
class java.lang.StringBuilder
interface java.lang.CharSequence

你会发现每一个类型参数都被保留了,而且在运行期可以通过反射机制获取到。那么到底什么是“类型擦除”?至少某些东西被擦除了吧?是的。事实上,除了结构化信息外的所有东西都被擦除了 —— 这里结构化信息是指与类结构相关的信息,而不是与程序执行流程有关的。换言之,与类及其字段和方法的类型参数相关的元数据都会被保留下来,可以通过反射获取到。

而其他的信息都被擦除掉了。例如下面这段代码:

 List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
}

实际上会被转换成这个(这两段代码的字节码是一致的)

 List list = new ArrayList();
Iterator it = list.iterator();
while (it.hasNext()) {
String s = (String) it.next();
}
 

因此,定义在方法体内的类型参数会被擦除,在必要的时候会有类型转换。另外,如果一个方法被定义为接受 List 参数,这个 T 会被转换成 Object (如果定义了类型的上界的话就转换成对应的类型。这也是你不能 new T() 的原因)。(顺便这里有个关于类型擦除的问题)

目前为止类型擦除定义中的前两点我们都讲完了。第三点是关于bridge方法,我已经在 stackoverflow 上的这个问题(和回答)中已经说明了。

总结:

  第一,Java泛型是非常复杂的。但是不用完全理解这些细节也可以使用它们。

  第二,不要假设所有的类型信息都被擦除了 —— 结构化的类型参数还存在,需要的话还是可以用下的(不过不要过分依赖反射机制)。

Java泛型类型擦除与运行时类型获取的更多相关文章

  1. Java基础之RTTI 运行时类型识别

    运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于R ...

  2. Java泛型函数的运行时类型检查的问题

    在一个数据持久化处理中定义了数据保存和读取的 泛型函数的,但是在运行时出现类型转换错误,类型不匹配,出错的位置不是load方法,而是在调用load方法之后,得到了列表数据,对列表数据进行使用时出现的. ...

  3. Java进阶(四)Java反射TypeToken解决泛型运行时类型擦除问题

    在开发时,遇到了下面这条语句,不懂,然习之. private List<MyZhuiHaoDetailModel> listLottery = new ArrayList<MyZhu ...

  4. java多态的向上转型与向下转型(与编译时类型与运行时类型有关)

    1.编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定. 当编译时类型和运行时类型不一致时,就会出现所谓的多态. 因为子类是一个特殊的父类,因此java允许把一个子类对象直接 ...

  5. Java泛型类型擦除问题

    以前就了解过Java泛型的实现是不完整的,最近在做一些代码重构的时候遇到一些Java泛型类型擦除的问题,简单的来说,Java泛型中所指定的类型在编译时会将其去除,因此List 和 List 在编译成字 ...

  6. Java 泛型优点之编译时类型检查

    Java 泛型优点之编译时类型检查 使用泛型代码要比非泛型代码更有优势,下面是 Java 官方教程对泛型其中一个优点的介绍: "Stronger type checks at compile ...

  7. 【JavaSE】运行时类型信息(RTTI、反射)

    运行时类型信息使得你可以在程序运行时发现和使用类型信息.--<Think in java 4th> **** 通常我们在面向对象的程序设计中我们经常使用多态特性使得大部分代码尽可能地少了解 ...

  8. 介绍下Java内存区域(运行时数据区)

    介绍下Java内存区域(运行时数据区) Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域.JDK 1.8 和之前的版本略有不同. 下图是 JDK 1.8 对JV ...

  9. RTTI 运行时类型识别 及异常处理

    RTTI   运行时类型识别 typeid  ------  dynamic_cast dynamic_cast 注意事项: 1.只能应用于指针和引用之间的转化 2.要转换的类型中必须包含虚函数 3. ...

随机推荐

  1. 1(1).有监督 VS 无监督

    对比一 : 有标签 vs 无标签 有监督机器学习又被称为“有老师的学习”,所谓的老师就是标签.有监督的过程为先通过已知的训练样本(如已知输入和对应的输出)来训练,从而得到一个最优模型,再将这个模型应用 ...

  2. 【西北师大-2108Java】第十次作业成绩汇总

    [西北师大-2108Java]第十次作业成绩汇总 作业题目 面向对象程序设计(JAVA) 第12周学习指导及要求 实验目的与要求 (1)掌握Vetor.Stack.Hashtable三个类的用途及常用 ...

  3. C++ explicit关键字,修饰构造函数,ctor

    #include <iostream> // operator Type() 类型操作符重载 // operator int() // operator double() // ... / ...

  4. C# 对 Excel 的相关操作

    C# 对Excel的操作 学习自: 教练辅导 C# 对Excel的读取操作 我们需要额外添加引用: References 搜索Excel 这样我们的基础就添加完成了. 并且在using 中添加: us ...

  5. 批量装机工具cobbler简介及其安装使用

    前言:如果仅有几台机器的话,使用U盘或者光盘装起来还不是很费事,一旦数量到了一定程度,使用手动方法就是一件费时费力的事,PXE+kistart就可以解决这个问题,降低难度,加快速度,而cobbler更 ...

  6. 【带着canvas去流浪(9)】粒子动画

    目录 一. 粒子特效 二. 开发中遇到的问题 2.1 卡顿 2.2 轨迹 2.3 复位 2.4 防护层 2.5 二维向量类 三. 实现讲解 3.1 粒子类的update方法 3.2 粒子群的绘制 3. ...

  7. TortoiseSVN 无法查看日志 日期显示1970-01-01的解决方案

    对有问题的目录, 在右键菜单中, 使用 TortoiseSVN 的 Revision Graph 命令, 等待该命令执行完毕, 再重新使用 Show Log 命令, 即可查看日志了

  8. MyBatis的结构和配置

    概述 MyBatis将用户从JDBC的访问中解放出来,用户只需要定义需要操作的SQL语句,无须关注底层的JDBC操作,就可以面向对象的方式进行持久层操作.底层数据库连接的获取.数据访问的实现.事务控制 ...

  9. frp内网渗透实现ssh外网访问家里树莓派(树莓派raspbian系统+腾讯云contos7)

    只有信用卡大小的它,同时也是一台功能完备的电脑(树莓派),把内网能玩的功能都玩了个遍,自然就有了外网访问这台树莓派的需求.一样也是查阅了无数文章,研究了无数个方案,最终试验成功用FRP实现了内网穿透, ...

  10. 【新人填坑008】django升级2.x后报'WSGIRequest' object has no attribute 'session',

    1.X 到2.x后中间件定义的名字也有所不同 改一下就好了 在setting文件中将原先的 MIDDLEWARE_CLASS 改成MIDDLEWARE 如果还运行不成功  注释掉标白线的那一行 删掉也 ...