广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java泛型类型擦除
  • 268
分享到

Java泛型类型擦除

2024-04-02 19:04:59 268人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录1. 什么是类型擦除2. 什么是原始类型3. 类型擦除带来的问题和解决方法3.1 和多态的冲突3.2 不能使用instanceof3.3 在静态类和静态方法中的问题1. 什么是类

1. 什么是类型擦除

Java 泛型(generics)是 jdk 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

Java的泛型是伪泛型,Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程称为类型擦除。

如在代码中定义List<Object>和List<String>等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法在运行时刻出现的类型转换异常的情况,类型擦除也是Java的泛型与c++模板机制实现方式之间的重要区别。

例1:

public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("string");
        List<Integer> integerList = new ArrayList<>();
        integerList.add(1);
        System.out.println(stringList.getClass() == integerList.getClass());
    }

输出true

这里我们定义两个ArrayList,类型分别是String和Integer,通过.getClass()获取他们的类信息,结果是相等,说明类型被擦除了

例2

public static void main(String[] args) throws Exception{
        List<String> stringList = new ArrayList<>();
        stringList.add("string");
//        stringList.add(1); //这里如果直接添加int类型的参数,会报错
        stringList.getClass().getMethod("add", Object.class).invoke(stringList, 1);
        System.out.println(stringList.toString());
    }

输出[string, 1]

这里我们定义了一个String类型的ArrayList,如果直接调用add()方法添加Integer类型的参数,会报错。我们使用反射调用方法add(),可以添加Integer类型的参数,说明String类型在编译后被擦除了

2. 什么是原始类型

原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

class Pair<T> {  
    private T value;  
    public T getValue() {  
        return value;  
    }  
    public void setValue(T  value) {  
        this.value = value;  
    }  
}

Pair的原始类型为

class Pair {  
    private Object value;  
    public Object getValue() {  
        return value;  
    }  
    public void setValue(Object  value) {  
        this.value = value;  
    }  
}

在Pair<T>中,T是一个无限定的类型变量,所以用Object替换,其结果就是一个普通的类,如同泛型加入Java语言之前的已经实现的样子。在程序中可以包含不同类型的Pair,如Pair<String>或Pair<Integer>,但是擦除类型后他们的就成为原始的Pair类型了,原始类型都是Object。

如果类型变量有限定,那么原始类型就用第一个边界的类型变量类替换。

如:

public class Pair<T extends Comparable> {}

那么原始类型就是Comparable

在调用泛型方法时,可以指定泛型,也可以不指定泛型。

在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一父类的最小级,直到Object
在指定泛型的情况下,该方法的几种类型必须是该泛型的实例的类型或者其子类

public static void main(String[] args) {  

          
        int i = Test.add(1, 2); //这两个参数都是Integer,所以T为Integer类型  
        Number f = Test.add(1, 1.2); //这两个参数一个是Integer,一个是Float,所以取同一父类的最小级,为Number  
        Object o = Test.add(1, "asd"); //这两个参数一个是Integer,一个是Float,所以取同一父类的最小级,为Object  

          
        int a = Test.<Integer>add(1, 2); //指定了Integer,所以只能为Integer类型或者其子类  
        int b = Test.<Integer>add(1, 2.2); //编译错误,指定了Integer,不能为Float  
        Number c = Test.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float  
    }  

    //这是一个简单的泛型方法  
    public static <T> T add(T x,T y){  
        return y;  
    }

3. 类型擦除带来的问题和解决方法

3.1 和多态的冲突

有这样一个泛型类

class Pair<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

一个子类继承它

class DateSon extends Pair<Date> {
    @Override
    public void setValue(Date value) {
        super.setValue(value);
    }
    @Override
    public Date getValue() {
        return super.getValue();
    }
}

在子类中,我们设定泛型类型为Date,那么父类的两个方法参数类型都是Date

public Date getValue() {
   return value;
}

public void setValue(Date value) {
   this.value = value;
}

我们从代码编译来看,@Override重写父类方法,没有任何问题。

但是,类型擦除后,父类的泛型类型都变为了Object,编译后为

class Pair {  
    private Object value;  

    public Object getValue() {  
        return value;  
    }  

    public void setValue(Object  value) {  
        this.value = value;  
    }  
}  

子类的重写方法

@Override
public void setValue(Date value) {
    super.setValue(value);
}
@Override
public Date getValue() {
    return super.getValue();
}

参数类型不一样,参数名一样… 根据Java语言特性,这应该是重载,而不是重写啊

写一个方法测试一下

public static void main(String[] args) {
    DateSon dateSon = new DateSon();
    dateSon.setValue(new Date());
    dateSon.setValue(new Object()); // 编译报错 
}

如果是重载,第一个和第二个setValue都应该编译通过,但是发现并没有继承父类setValue参数类型是Object的方法,所以说确实是重写

我们本意是通过设置泛型类型为Date,实现重写,但是这和泛型的类型擦除,显然冲突了。

针对这种冲突,JVM采取了一种特殊的方法,桥方法

我们用javap -c className的方式反编译下DateSon子类的字节码,结果如下:

class com.java.generic.DateSon extends com.java.generic.Pair<java.util.Date> {
  com.java.generic.DateSon();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method com/java/generic/Pair."<init>":()V
       4: return

  public void setValue(java.util.Date);
    Code:
       0: aload_0
       1: aload_1
       2: invokespecial #2                  // Method com/java/generic/Pair.setValue:(Ljava/lang/Object;)V
       5: return

  public java.util.Date getValue();
    Code:
       0: aload_0
       1: invokespecial #3                  // Method com/java/generic/Pair.getValue:()Ljava/lang/Object;
       4: checkcast     #4                  // class java/util/Date
       7: areturn

  public void setValue(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #4                  // class java/util/Date
       5: invokevirtual #5                  // Method setValue:(Ljava/util/Date;)V
       8: return

  public java.lang.Object getValue();
    Code:
       0: aload_0
       1: invokevirtual #6                  // Method getValue:()Ljava/util/Date;
       4: areturn
}

最后的两个方法,就是编译器自己生成的桥方法。可以看到桥方法的参数类型都是Object,也就是说,子类中真正覆盖父类两个方法的就是这两个我们看不到的桥方法。而打在我们自己定义的setvalue和getValue方法上面的@Oveerride只不过是假象。而桥方法的内部实现,就只是去调用我们自己重写的那两个方法。

所以,虚拟机巧妙的使用了桥方法,来解决了类型擦除和多态的冲突。

3.2 不能使用instanceof

List<String> stringList = new ArrayList<>();
System.out.println(stringList instanceof ArrayList<String>);

类型擦除后String类型不存在了,所以不能使用instanceof判断

3.3 在静态类和静态方法中的问题

public class Test2<T> {    
    public static T one;   //编译错误    
    public static  T show(T one){ //编译错误    
        return null;    
    }    
}

因为泛型类中的泛型参数的实例化是在定义对象的时候指定的,而静态变量和静态方法不需要使用对象来调用。对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的。

但是注意:

public class Test2<T> {    

    public static <T> T show(T one){ //这是正确的    
        return null;    
    }    
}

因为这是一个泛型方法,在泛型方法中使用的T是自己在方法中定义的T,而不是泛型类中的T

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

--结束END--

本文标题: Java泛型类型擦除

本文链接: https://www.lsjlt.com/news/142610.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java泛型类型擦除
    目录1. 什么是类型擦除2. 什么是原始类型3. 类型擦除带来的问题和解决方法3.1 和多态的冲突3.2 不能使用instanceof3.3 在静态类和静态方法中的问题1. 什么是类...
    99+
    2022-11-13
  • Java中怎么擦除泛型类型
    Java中怎么擦除泛型类型,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。前言Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采...
    99+
    2023-06-20
  • Java的类型擦除式泛型详解
    Java选择的泛型类型叫做类型擦除式泛型。什么是类型擦除式泛型呢?就是Java语言中的泛型只存在于程序源码之中,在编译后的字节码文件里,则全部泛型都会被替换为原来的原始类型(Raw...
    99+
    2022-11-12
  • java泛型中类型擦除怎么转换
    今天小编给大家分享一下java泛型中类型擦除怎么转换的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。说明泛型值存在于编译阶段,...
    99+
    2023-06-30
  • Java泛型的类型擦除怎么理解
    今天小编给大家分享一下Java泛型的类型擦除怎么理解的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解...
    99+
    2022-10-19
  • Java泛型之类型擦除实例详解
    目录前言泛型是什么?泛型的定义和使用泛型类泛型方法泛型类与泛型方法的共存现象泛型接口通配符?无限定通配符<><extendsT>类型擦除类型擦除带来的局限性泛...
    99+
    2022-11-13
  • Java泛型的类型擦除示例详解
    目录前言泛型的类型擦除原则是:1 擦除类定义中的类型参数1.1 无限制类型擦除1.2 有限制类型擦除2 擦除方法定义中的类型参数3 桥接方法和泛型的多态总结参考资料前言 Java泛型...
    99+
    2022-11-12
  • 怎么用Java泛型实现类型擦除
    本篇内容主要讲解“怎么用Java泛型实现类型擦除”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用Java泛型实现类型擦除”吧!前言先给大家奉上一道经典的测试题。List<String&...
    99+
    2023-06-29
  • java泛型中类型擦除的转换示例
    这篇文章给大家分享的是有关java泛型中类型擦除的转换示例的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。java基本数据类型有哪些Java的基本数据类型分为:1、整数类型,用来表示整数的数据类型。2、浮点类型,用...
    99+
    2023-06-14
  • Java 泛型考古 泛型擦除 包装类详细解析
    目录一. 什么是泛型二. 为什么要有泛型 ?示例三、泛型考古四、泛型擦除五、包装类六、装箱拆箱一. 什么是泛型 泛型(generic type)其本质是将类型参数化,也就是说所操作的...
    99+
    2022-11-13
  • Java泛型中类型擦除问题怎么解决
    这篇“Java泛型中类型擦除问题怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java泛型中类型擦除问题怎么解决”文...
    99+
    2023-06-30
  • Java泛型擦除是什么
    这篇文章主要介绍了Java泛型擦除是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java泛型擦除是什么文章都会有所收获,下面我们一起来看看吧。泛型信息只存在于代码编译阶段,但是在java的运行期(已经生成...
    99+
    2023-06-27
  • Java语法关于泛型与类型擦除的分析
    泛型与类型擦除 泛型,JDK 1.5新特性,本质是参数化类型(Parametersized Type) 的应用,即所操作的数据类型被指定为一个参数。这种参数类型可用在: 类...
    99+
    2022-11-12
  • Java中类型擦除式泛型的作用是什么
    本篇文章给大家分享的是有关Java中类型擦除式泛型的作用是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java选择的泛型类型叫做类型擦除式泛型。什么是类型擦除式泛型呢?就...
    99+
    2023-06-20
  • 详解java 中泛型中的类型擦除和桥方法
    在Java中,泛型的引入是为了在编译时提供强类型检查和支持泛型编程。为了实现泛型,Java编译器应用类型擦除实现:       1、  用类型参数(type parame...
    99+
    2023-05-31
    java 泛型 桥方法
  • 详解Java泛型中类型擦除问题的解决方法
    以前就了解过Java泛型的实现是不完整的,最近在做一些代码重构的时候遇到一些Java泛型类型擦除的问题,简单的来说,Java泛型中所指定的类型在编译时会将其去除,因此List&nbs...
    99+
    2022-11-13
  • Java中泛型擦除的示例分析
    小编给大家分享一下Java中泛型擦除的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.问题引出源码: public static&n...
    99+
    2023-05-31
    java
  • Java泛型擦除的问题怎么解决
    这篇文章主要介绍了Java泛型擦除的问题怎么解决,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。java基本数据类型有哪些Java的基本数据类型分为:1、整数类型,用来表示整数...
    99+
    2023-06-14
  • Java中泛型擦除的原理是什么
    这篇文章将为大家详细讲解有关Java中泛型擦除的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和面向对象...
    99+
    2023-06-14
  • 关于泛型擦除问题的解决--Mybatis查询类型转换
    目录概念介绍问题案例原因分析解决方案总结概念介绍 Java语言的泛型采用的是擦除法实现的伪泛型,泛型信息(类型变量、参数化类型)编译之后通通被除掉了。使用擦除法的好处就是实现简单、非...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作