Thinking in java学习笔记—泛型(擦除)

来源:互联网 发布:数据库isnull 编辑:程序博客网 时间:2024/06/06 17:45

一、泛型擦除

在泛型代码内部,无法获得任何有关泛型参数类型的消息。

import java.util.Arrays;class AA<T>{}class CC<Q>{}class B{}public class Generic {public static void main(String[] args){AA<B> a  = new AA<B>();CC<B> c  = new CC<B>(); System.out.println(Arrays.asList(a.getClass().getTypeParameters())); System.out.println(Arrays.asList(c.getClass().getTypeParameters()));}}/*Output:[T][Q]*///:~
上面这段代码,通过反射我们查看在泛型类对象的参数类型,最后输出的结果只是用作参数占位符的标识符,这不是有用的信息。

Java泛型是使用擦除来实现的,这意味着当我们使用泛型时,任何具体的类型信息都将被擦除,唯一知道的就是我们在使用一个对象。

List<String>和List<Integer>在运行时实际上都是相同的类型。


二、应对擦除

在基于擦除性的实现中,泛型类被当做第二类型处理,即不能再某些重要的上下文环境中使用的类型。泛型类型只有在静态类型检查期间才出现。

任何在运行时需要知道确切类型信息的操作都将无法工作:

public class Erased<T> {private final int SIZE =  100;public static void f(Object arg){if(arg instanceof T){}   //ErrorT var = new T();         //ErrorT[] array = new T[SIZE]; //ErrorT[] array = (T)new Object[SIZE];  //Unchecked warning}}

可以通过引入类型标签来对擦除进行补偿。这意味着需要显式地传递类型的Class对象。

import java.lang.reflect.Array;import java.util.Arrays;public class ArrayMaker<T>{private Class<T> kind;                   //在泛型类内部,我们用一个class域来保存类型class对象。public ArrayMaker(Class<T> kind){this.kind = kind;}@SuppressWarnings("unchecked")public T[] creat(int size){return (T[])Array.newInstance(kind,size);  //反射机制  ,当我们想要创建一个泛型数组时,                                           //可以通过reflect里面的Array.newInstance(Class<?> component,int length)                                           //来达成目的}public static void main(String[] args){ArrayMaker<String> make = new ArrayMaker<String>(String.class);String[] str = (String[])make.creat(10); System.out.println(Arrays.toString(str));}}
接下来我们来看一下面对擦除,我们有哪些解决方法


1、创建类型示例

(1)传递一个工厂对象,并使用它来创建新的实例

最便利的工厂方法就是就是Class对象

class TestClass{public TestClass(){System.out.println("I am TestClass constructor");}}public class ClassAsFactory<T>{private T t;public ClassAsFactory(Class<T> cla){try{t = cla.newInstance();}catch(InstantiationException e){e.printStackTrace();}catch(IllegalAccessException e){    e.printStackTrace();}}public static void main(String[] args){ClassAsFactory newclass = new ClassAsFactory(TestClass.class);}}/*OutputI am TestClass constructor*///~


(2)但是碰到没有默认构造器的类型,这种方式就会失效了,我们可以使用显示的工厂方法

import java.util.Random;interface Factory<T>{                                                  //这是工厂方法的工厂接口T creat();}class IntegerFactory implements Factory<Integer>{                     //实现了Factory接口的类public Integer creat(){return new Integer(new Random(47).nextInt());}}class InnerFactory{public static class innerFactory implements Factory<InnerFactory>{  //内部类实现Factorypublic InnerFactory creat(){return new InnerFactory();}}}public class FactoryCreatTest<T> {private T t;public <F extends Factory<T>> FactoryCreatTest(F f){  //将参数类型限制为Factory<T>t = f.creat();                                    //通过工厂方法,我们实现了泛型实例的创建}public static void main(String args){new FactoryCreatTest(new IntegerFactory());new FactoryCreatTest(new InnerFactory.innerFactory());}}
2、泛型数组

前面说过,可以利用反射机制里面的Array.newInstance(CLass<?> component,int lenght)来在泛型类内部创建泛型数组,但是这种方法要求传递类型标签,

即类型的Class对象。

数组将跟踪它们的实际类型,而这个类型是在数组被创建时确定的。

因此下面通过创建Object数组然后将其转型为某个类型会产生ClassCaseException

class AFGone<T>{}public class ArrayOfGeneric {static int SIZE =  100;static AFGone<Integer>[] afg;public static void main(String[] args){@SuppressWarnings("unchecked")//Compiles:produces ClassCastException:    //!afg = (AFGone<Integer>[])new Object[SIZE];}}
public class GenericArray<T> {  private T[] array;  @SuppressWarnings("unchecked")                     //如果将SupressWarnings去掉,在这里就会产生警告。  public GenericArray(int sz) {    array = (T[])new Object[sz];                      }  public void put(int index, T item) {    array[index] = item;  }  public T get(int index) { return array[index]; }  // Method that exposes the underlying representation:  public T[] rep() { return array; }<span style="white-space:pre"></span>  public static void main(String[] args) {    GenericArray<Integer> gai =      new GenericArray<Integer>(10);    // This causes a ClassCastException:            //因为运行时的实际类型是Object[]    //! Integer[] ia = gai.rep();    // This is OK:    Object[] oa = gai.rep();  }} ///:~

下面是几种解决方法:

(1)使用ArrayList
(2)通过创建有一个被擦除类型的数组,然后对其进行转型

class AFGone<T>{}public class ArrayOfGeneric {static int SIZE =  100;static AFGone<Integer>[] afg;public static void main(String[] args){afg = (AFGone<Integer>[])new AFGone[SIZE];           //创建泛型数组的方式是创建一个被擦除类型的新数组,然后对其进行转型。System.out.println(afg.getClass().getSimpleName());}}
上面这段代码,类型AFGone<Integer>在运行时将被擦除到AFGone。


(3)最后,我们可以在内部将数组贮存为Object[],当我们要返回具体类型时,再将他进行转型。

public class ArrayOfGeneric<T> {static int SIZE =  100;static Object[] afg;@SuppressWarnings("unchecked")public T[] get(){return (T[])afg;                  //在使用时再将其进行转型}}









0 0
原创粉丝点击