• Java泛型(5):擦除与补偿


    先看一个例子:

    Class<?> c1 = new ArrayList<String>().getClass();
    Class<?> c2 = new ArrayList<Integer>().getClass();
    System.out.println(c1 == c2); // true

    虽然泛型类的参数不同,但是结果却是TRUE。这是因为在泛型代码内部,无法获得任何有关泛型参数类型的信息

    Java泛型是通过擦除来实现的。这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的是你在使用一个对象。因此List<String>和List<Integer>在运行时事实上都被擦除成原生的List类型。

    运行时,使用泛型和使用Object所产生的字节码是相同的。因此,泛型是在编译期对代码进行检查的。

    擦除丢失了在泛型代码中执行某些操作的能力。尽管你一厢情愿想地想把泛型参数替换成为你想要的类型,但是实际上它什么都不是。任何在运行时需要知道确切类型信息的操作都将无法工作。

    T t = new T(); // Compile error
    T[] t = new T[](); // Compile error
    if(c1 instanceof T) {} // Compile error

    可以通过引用类型参数来对擦除进行补偿。即显式地传递该类型的class对象。下面介绍了3种方法:

    (1) 直接传递一个Class<T>的内建工厂对象缺点是如果T实例化(newInstance();)失败,编译期不能捕获异常

     1 class ClassAsFactory<T> {
     2     T x;
     3     public ClassAsFactory(Class<T> kind) {
     4         try {
     5             x = kind.newInstance();
     6         } catch (Exception e) {
     7             throw new RuntimeException(e);
     8         }
     9     }
    10 }
    11 
    12 class Employee { }
    13 
    14 public class InstantiateGenericType {
    15     public static void main(String[] args) {
    16         new ClassAsFactory<Employee>(Employee.class);
    17         System.out.println("ClassAsFactory<Employee> succeeded");
    18         try {
    19             // Integer并没有默认构造器,所以调用newInstance()时会报Error
    20             new ClassAsFactory<Integer>(Integer.class);
    21         } catch (Exception e) {
    22             System.out.println("ClassAsFactory<Integer> failed");
    23         }
    24     }
    25 }

    (2) 创建一个显式的工厂对象它可以很好的限制对象类型。

     1 interface FactoryI<T> {
     2     T create();
     3 }
     4 
     5 class Foo<T> {
     6     public final T x;
     7 
     8     public <F extends FactoryI<T>> Foo(F factory) {
     9         x = factory.create();
    10     }
    11 }
    12 
    13 // 直接实现FactoryI接口
    14 class IntegerFactory implements FactoryI<Integer> {
    15     @Override
    16     public Integer create() {
    17         return new Integer(0);
    18     }
    19 }
    20 
    21 // 创建静态内部类实现FactoryI接口
    22 class Widget {
    23     public static class Factory implements FactoryI<Widget> {
    24         @Override
    25         public Widget create() {
    26             return new Widget();
    27         }
    28     }
    29 }
    30 
    31 public class FactoryConstraint {
    32     public static void main(String[] args) {
    33         new Foo<Integer>(new IntegerFactory());
    34         new Foo<Widget>(new Widget.Factory());
    35     }
    36 }

    (3) 使用模版方法设计模式创建抽象类,由具体的子类实现创建对象

     1 abstract class GenericWithCreate<T> {
     2     final T element;
     3     GenericWithCreate() {
     4         element = create();
     5     }
     6     abstract T create();
     7 }
     8 
     9 class Clazz { }
    10 
    11 class Creator extends GenericWithCreate<Clazz> {
    12     
    13     Creator() {
    14         System.out.println(element.getClass().getSimpleName());
    15     }
    16     
    17     Clazz create() {
    18         Clazz clazz = new Clazz();
    19         return clazz;
    20     }
    21 }
    22 
    23 public class CreatorGeneric {
    24     public static void main(String[] args) {
    25         new Creator(); // Clazz
    26     }
    27 }

    对于创建泛型数组的情况,一般来说使用ArrayList代替。成功创建泛型数组的唯一方法就是创建一个被擦除类型的新数组(T[] array = (T[]) new Object[size];),并且这个数组的类型只能是Object[]下面介绍了2种方法:

    (1) 使用new Object[size]。缺点是由于擦除,我们无法判定创建好的数组的具体类型。例子中如果改成Integer,则会抛出ClassCastException。

     1 public class GenericArray<T> {
     2     private T[] array;
     3 
     4     @SuppressWarnings("unchecked")
     5     public GenericArray(int size) {
     6         array = (T[]) new Object[size];
     7     }
     8 
     9     public void put(int index, T item) {
    10         array[index] = item;
    11     }
    12 
    13     public T get(int index) {
    14         return (T) array[index];
    15     }
    16 
    17     public T[] rep() {
    18         return (T[]) array;
    19     }
    20 
    21     public static void main(String[] args) {
    22         GenericArray<Integer> gai = new GenericArray<Integer>(10);
    23         for (int i = 0; i < 10; i++)
    24             gai.put(i, i);
    25         // Integer[] ia = gai.rep(); // [ERROR] java.lang.ClassCastException
    26         Object[] ia = gai.rep();
    27         for (int i = 0; i < ia.length; i++)
    28             System.out.print(ia[i] + " "); // 0 1 2 3 4 5 6 7 8 9 
    29     }
    30 }

    (2) 使用Array.newInstance(type, size)。这时需要传入一个类型标记,就可以解决(1)中的问题。

     1 import java.lang.reflect.Array;
     2 
     3 public class GenericArrayWithTypeToken<T> {
     4     private T[] array;
     5 
     6     @SuppressWarnings("unchecked")
     7     public GenericArrayWithTypeToken(Class<T> type, int size) {
     8         array = (T[]) Array.newInstance(type, size);
     9     }
    10 
    11     public void put(int index, T item) {
    12         array[index] = item;
    13     }
    14 
    15     public T get(int index) {
    16         return array[index];
    17     }
    18 
    19     public T[] rep() {
    20         return array;
    21     }
    22 
    23     public static void main(String[] args) {
    24         GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<Integer>(Integer.class, 10);
    25         for (int i = 0; i < 10; i++)
    26             gai.put(i, i);
    27         Integer[] ia = gai.rep(); // It's OK
    28         for (int i = 0; i < ia.length; i++)
    29             System.out.print(ia[i] + " "); // 0 1 2 3 4 5 6 7 8 9 
    30     }
    31 }
  • 相关阅读:
    org.hibernate.HibernateException: No Session found for current thread
    TCP/IP协议 HTTP协议
    [ERROR][org.springframework.web.context.ContextLoader][main] Context initialization failed org.sprin
    oracle 导出表结构和数据,使用plsql
    jar包里查找指定的class文件,排查是否存在或重复,工具软件:Java Class Finder
    maven手动安装jar到本地仓库
    The reference to entity “idNo” must end with the ';' delimiter 异常处理
    activeMQ下载,安装,启动,关闭
    Oracle错误:ORA-01033
    -Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m
  • 原文地址:https://www.cnblogs.com/storml/p/7930565.html
Copyright © 2020-2023  润新知