Java中泛型——类型擦除

问题导引

这个例子里,定义了两个List集合,不过一个是List泛型类型的,只能存储整数;一个 是List泛型类型的,

只能存储字符串。最后我们通过list1对象和list2对象的 getClass()方法获取他们的类的信息,结果发现结

果为true。说明泛型类型String和 Integer都被擦除掉了,只剩下原始类型。

例如如下代码:

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.fanxing;

import java.util.ArrayList;
import java.util.List;

public class  FanTest  { 
    public static void main(String[] args) { 
        List<Integer> list1 = new ArrayList<Integer>();
        List<String> list2 = new ArrayList<String>();
        System.out.println(list1.getClass() == list2.getClass());
    }
}

他的运行结果是true:

这就是类型擦除,下面咱们来详细查看一下:

类型擦除概念

泛型是java1.5版本才引进的概念,在这之前没有泛型,但是,泛型代码能够很好的和之前版本的代码兼容,原因是泛型只存在代码编译阶段,在进入JVM 之前,泛型相关的信息会被擦除掉,我们称之为类型擦除

类型擦除应用场景

需要值得注意的一点是:类型擦除后保留的原始类型

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

看下面两个例子理解一下:

案例:原始类型被Object替换

代码如下(不能运行,就是看一下这个特性)

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Pair<T>  { 
    //原始类型就是Object
    private T value;

    public  T  getValue()  { 
        return value;
    }

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

    public  static  void  main(String[] args)  { 
        Pair pair = new Pair();
        pair.setValue();//调用setter方法能看到参数类型是 Object:setValue(Object) ctrl+鼠标放到setValue()中能看到setValue(Object)
    }
}

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

案例:原始类型被限定类型替换

我们可以用extends设置上届

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Pair<T  extends  Comparator>  { 
    //原始类型就是Comparator
    private T value;

    public T getValue() { 
        return value;
    }

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

    public static void main(String[] args) { 
        Pair pair = new Pair();
        pair.setValue();//调用setter方法能看到参数类型是 Comparator:setValue(Comparator)
    }
}

这里相应的原始类型都会被自动提供了,原始类型就用第一个边界的类型替换,也就是Comparator

类型擦除的限制

无法利用同一泛型类的实例区分方法签名

例如下面代码,就会报错:

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Erasure { 
    //虽然泛型实例不同,但是由于类型擦除后是同一字节码(类型擦拭之后,就都是 List了),因此不能 用于区分方法签名
    public  void  test(List<String> list)  { 
        System.out.println("String");
    }

    public  void  test(List<Integer> list)  { 
        System.out.println("Integer");
    }
}

虽然泛型实例不同,但是由于类型擦除后是同一字节码(类型擦拭之后,就都是 List了),因此不能 用于区分方法签名 +

泛型类的静态变量是共享的

例如下面代码:

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  StaticTest  { 
    public static void main(String[] args) { 
        System.out.println(GT.var);
        GT<Integer> gti = new GT<>();
        gti.var = 1;
        System.out.println(GT.var);
        GT<String> gts = new GT<>();
        gts.var = 2;//泛型参数不同,但是共享类的静态成员
        System.out.println(GT.var);
    }
}

class GT<T>  { 
    public static int var = 0;

    public void nothing(T x) { 
    }
}

结果如下所示,泛型参数不同,但是共享类的静态成员:

类型擦除的特征

  • 所有泛型类的类型参数在编译时都会被擦除,虚拟机运行时中没有泛型,只有普通类和普通方法,从这一点上来说,Java中的泛型从某种程度上是一种语法糖

  • Java泛型不支持基本类型 例如: short int double等

  • 在泛型代码内部,无法获得任何有关泛型参数类型的信息,如果传入的类型参数为T,那么在泛型代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了

  • 创建泛型对象时请指明类型,让编译器尽早的做参数检查

  • 不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你

  • Java的泛型类型不能用于new构建对象(也不能用于初始化数组).泛型不能用于显性地引用运行时类型的操作之中,例如转型,instanceof和new操作(包括new一个对象,new一个数组),因为所有关于参数的类型信息都在运行时丢失了,所以任何在运行时需要获取类型信息的操作都无法进行工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值