zoukankan      html  css  js  c++  java
  • [改善Java代码]Java的泛型是类型擦除的

    泛型可以减少强制类型的转换,可规范集合的元素类型,还可以提高代码的安全性和可读性,正是因为有了这些优点,自从Java引入泛型之后,项目的编码规则上便多了一条,优先使用泛型.

    Java泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,它与C++中的模板templates比较类似.但是有一点,Java的反省在编译期有效,在运行期被删除,也就是说所有的泛型参数类型在编译后都会被清除掉.

    看如下代码:

    import java.util.List;
    
    public class Foo {
        //arrayMethod接受数组参数,并进行重载
        public void arrayMethod(String[] strArray){}
        public void arrayMethod(Integer[] intArray){}
        //listMethod接收泛型List参数,并进行重载
        public void listMethod(List<String> strList){}
        public void listMethod(List<Integer> intList){}
    }

    编写了4个方法,arrayMethod方法接收String数组和Integer数组,这是一个典型的重载,listMethod接收元素类型为String和Integer的List变量.

    但是这段代码不能通过编译,不能通过通过编译是在listMethod方法上.

    提示:Erasure of method listMethod(List<String>) is the same as another method in type Foo  &&  Erasure of method listMethod(List<Integer>) is the same as another method in type Foo

    是说listMethod对应的这两个方法在编译时擦除类型后的方式是listMethod(List<E>),它与另外一个方法重复,通俗的说就是方法签名重复,这就是Java泛型擦除引起问题:在编译后所有的泛型类型都会做相应的转化.

    转换规则如下:

    1.List<String>,List<Integer>,List<T>泛型擦除后的类型为List.

    2.List<String>[]擦除之后的类型是List[]

    3.List<? extends E>,List<? super E>擦除之后对应类型是List<E>

    4.List<T extends Serializable & Cloneable>擦除之后为List<Serializable>

    Java编译后的字节码中已经没有泛型的任何信息了,也就说一个泛型类和一个普通类在经过编译后都指向了同一字节码,比如Foo<T>类,经过编译后将只有一份Foo.class类.

    不管是Foo<String>还是Foo<Integer>引用的都是同一个字节码,Java之所以这样处理有两方面原因:

    1.避免JVM大换血.C++的泛型生命期延续到了运行期,而Java是在编译器擦除掉的,如果JVM也把泛型类型延续到运行期,那么JVM就需要进行大量的重构工作了.

    2.版本兼容,在 编译期擦除可以更好的支持原生类型RawType,在Java1.5或1.6平台上,即使声明一个List这样的原生类型也是可以正常编译通过的,只是会产生警告信息而已.

    这样就可以解释如下问题了:

    (1)泛型的class对象都是相同的.

    每个类都有一个class属性,泛型化不会改变class属性的返回值.例如:

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 public class Client {
     5     public static void main(String[] args) {
     6         List<String> ls = new ArrayList<String>();
     7         List<Integer> li = new ArrayList<Integer>();
     8         System.out.println(li.getClass() == li.getClass());
     9     }
    10 }

     以上代码会返回true,List<String>和List<Integer>擦除后的类型都是List,没有任何的区别.

    (2)泛型数组初始化时不能声明泛型的类型

    原因很简单,可以声明一个带有泛型参数的数组,但是不能初始化该数组,因为执行了类型擦除操作,List<Object>[] 与List<String>就是一回事了,编译器拒绝如此的声明.

    (3)instanceof不允许穿在泛型参数

    原因 一样,泛型类型的被擦除了.

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 public class Client {
     5     public static void main(String[] args) {
     6         //List<String>[] listArray = new List<String>[];
     7         /*
     8         如上语句报错
     9         Multiple markers at this line
    10         - Variable must provide either dimension expressions or an array initializer
    11         - Cannot create a generic array of List<String>        
    12          */
    13         List<String>[] listArray2; //只声明不初始化没有问题.
    14         //======================================分割线============
    15         
    16         List<String> list = new ArrayList<String>();
    17         System.out.println(list instanceof List);//没有泛型就不会报错,
    18         //System.out.println(list instanceof List<String>);//有泛型就报错.
    19         /*
    20         Cannot perform instanceof check against parameterized type List<String>.
    21         Use the form List<?> instead since further generic type information will be erased at runtime 
    22          */
    23         
    24         List<String> list2 = new ArrayList<String>();
    25         List l = list2;
    26         l.add(123);
    27     }
    28 }
  • 相关阅读:
    代码开发,测试及发布
    需求改进&系统设计
    综合系统开发----需求分析
    自我介绍+课后6问
    动态规划: 最大m子段和问题的详细解题思路(JAVA实现)
    mybatis typealias 问题
    对偶微分形式
    ASP.NET Web API 直到我膝盖中了一箭【1】基础篇
    第一篇博客,写点什么好呢?
    docker之MySQL主从复制
  • 原文地址:https://www.cnblogs.com/DreamDrive/p/5624135.html
Copyright © 2011-2022 走看看