散装java 散装java
首页
  • Java基础
  • JVM
  • Java多线程
  • 知识点
  • 案例
  • Redis
  • RabbitMQ
  • Kafka
  • Elasticsearch
  • MySQL
  • Linux
  • Docker
  • Zookeeper
  • Nginx
  • Git
  • JMeter
  • Gradle
  • 常见BUG
  • 常见解决方案
  • 资源
  • 问答
💖支持
Gitee (opens new window)
首页
  • Java基础
  • JVM
  • Java多线程
  • 知识点
  • 案例
  • Redis
  • RabbitMQ
  • Kafka
  • Elasticsearch
  • MySQL
  • Linux
  • Docker
  • Zookeeper
  • Nginx
  • Git
  • JMeter
  • Gradle
  • 常见BUG
  • 常见解决方案
  • 资源
  • 问答
💖支持
Gitee (opens new window)
  • Java基础

    • Java 基础导读
    • Java 中运算符的优先级
    • 泛型

      • 什么是泛型擦除
        • 怎么理解泛型擦除
        • 举例说明
        • 既然泛型擦除有问题,为什么还要使用呢,用Object代替不行?
      • 什么是泛型的桥方法
      • 泛型通配符以及边界
  • Java集合

    • ArrayList
    • LinkedList
    • HashMap
  • Java并发-JUC

  • JVM

    • 什么是JVM
    • JVM的组成及其作用
    • 类加载机制&双亲委派
  • Java多线程

    • 多线程导读
  • Java
  • Java基础
  • 泛型
散装java
2022-11-24
目录

什么是泛型擦除

# 怎么理解泛型擦除

Java 的泛型机制其实是伪泛型,因为他只作用于 Java 程序编译期间(就是在你写代码的时候起作用);在运行期间并不存在。

编译器在编译期间会动态的将泛型 T 擦除为 Object ,或者将 T extends xxxClass 擦除为其限定类型 xxxClass 。

可以通过下述代码证明上面说法↓↓↓

@SuppressWarnings("all")
public class GenericTest1 {
    public static void main(String[] args) {
        Class<Demo> demoClass = Demo.class;
        // 拿到 Demo 类的属性,并输出
        Field[] declaredFields = demoClass.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field.getName() + ":" + field.getType());
        }
    }
}

class Demo<T> {
    T t;
    Integer id;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

输出为:

t:class java.lang.Object
id:class java.lang.Integer
1
2

# 举例说明

在我们创建不同类型的 List 时候,会放入不同的泛型;在进行 add 的时候,可以限制 add 的类型,但是他们的类在最终比较的时候确实相同的;这就是泛型擦除造成的。

ArrayList<String> stringList = new ArrayList<>();
ArrayList<Integer> integerList = new ArrayList<>();
// 这里的输出 其实 true
System.out.println(stringList.getClass() == integerList.getClass());
// 这里的输出为 class java.util.ArrayList
System.out.println(stringList.getClass());
// 这里的输出为 class java.util.ArrayList
System.out.println(integerList.getClass());
1
2
3
4
5
6
7
8

但是 add 的类型限制也并不是绝对的,我们在在他运行时候(反射),进行动态的传参,可以做到往 integerList 中添加String类型的代码。

List<Integer> integerList = new ArrayList<>();
integerList.add(1);
// 通过反射来调用
Method method = integerList.getClass().getDeclaredMethod("add", Object.class);
method.invoke(integerList, "散装java");
// 这里的输出为 [1, 散装java]
System.out.println(integerList.toString());
1
2
3
4
5
6
7

以上的代码都能证明,泛型是在运行期间被擦除的!

另外以下写法会报错,也是因为泛型擦除的存在。报错为 两种方法具有相同的擦除

// 方法重载失败
// 错误提示为 'method(List<Integer>)' clashes with 'method(List<String>)'; both methods have same erasure
public void method(List<Integer> list){}
public void method(List<String> list){}
1
2
3
4

# 既然泛型擦除有问题,为什么还要使用呢,用Object代替不行?

  1. 使用泛型可以在编译期间进行类型检测,尽早发现问题

  2. 使用Object 类型需要手动强制转换类型,而用泛型则可以节省了这个操作可以有效的避免classCastException,代码可读性更高,出错率更低

  3. 提升性能,编译完成后,基本就确定了类型,节省了强制类型转换带来的性能消耗

上次更新: 2022/11/27, 16:23:17
Java 中运算符的优先级
什么是泛型的桥方法

← Java 中运算符的优先级 什么是泛型的桥方法→

Theme by Vdoing | Copyright © 2022-2024 散装java | MIT License | 鲁ICP备2022022143号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式