Java泛型的局限性

来源:互联网 发布:网络摄像头id 编辑:程序博客网 时间:2024/05/17 23:24

Java泛型的局限性一般都是由类型擦除引起的。


1. 不能使用基本类型实例化类型参数

描述:不可以定义类似于Piar<int>这样的类。

原因:假设泛型类为Pair<T>,类型擦除以后为Pair,也即Pair<Object>,如果定义Pair<int>则会出错,因为int不能赋予Object类型的变量。

解决办法:使用Integer,Double, Boolean, Long,Short, Character, Byte, Float

2. 不能实例化类型参数

描述:不可以new T()

原因:假设T是String,然后你准备定义String string = new T(); 但编译器类型擦除之后,这条定义就变成String string = new Object(); 这明显不可以。

解决办法:使用反射。

public static <T> T createInstance(Class<T> clazz) {     try {         T result = clazz.newInstance();     }     catch (Exception e) {return null     }}
3. 不可以实例化类型变量的数组

描述:不可以定义T[] array = new T[10];

原因:类型擦除后上述定义变为Object[] array = new Object[10]; 这样一来我们可以将任何类型赋予array[0], 比如array[0] = "123"; 编译器不会报错,但运行时就有可能会出错了。

解决方法:使用反射。

public static <T extends Comparable<T>> T[] maxTwo(Class<T> arrayElementClazz) {       // Type safety: Unchecked cast from Object[] to T[]       return (T[]) Array.newInstance(arrayElementClazz, 2) ;  }  

4. 不可以定义泛型类的数组

描述:不可以定义Pair<String>[] pairs = new Pair<String>[10];

原因:类型擦除以后变为Pair[] pars = new Pair[10]; 然后我们可以赋予pairs[0] = new Pair<Integer>(); 没有编译错误,但存在运行时错误。

解决方法:使用ArrayList,ArrayList<Pair<String>> pairs = new ArrayList<Pair<String>>();

5.不可以对泛型类型进行类型检测,转化

描述:不可以用a instanceof Pair<String>,也不可以(Pair<String>)a

原因:类型擦除后上述代码变为 a instanceof Pair; (Pair)a,这明显不是我们的目的;同理对于Pair<String> pair; pair.getClass()总是返回Pair.class;

PS: Pair<String> stringPairs = ...; Pair<Integer> intPairs = ...; 那么stringPairs.getClass() == intPairs.getClass() 返回值为true

6. 不可以抛出或捕获泛型异常

描述:既不能抛出也不能捕获泛型类的对象。实际上,甚至泛型类型扩展throwable都是不合法的。以下都是不合法的

public class Problem<T> extends Exception {/*...*/} 
public static <T extends Throwable> void doWork(Class<T> t) {   try {      do work   }   catch (T e) {      Logger.global.info(...)   }}
但以下方法合法:

public static <T extends Throwable> void doWork(T t) throws T {   try {      do work  }  catch (Throwable realCause) {       t.initCause(realCause);       throw t;  }}

7. 泛型类的静态类型变量/方法无效

描述:不可以在泛型类Pair<T>中定义public static T singleInstance;也不可以定义public static T getSingleInstance();

原因:擦除后变成

public Pair {public static Object singleInstance; public static object getSingleInstance();}
所以类型变量对于static元素并不起作用。

8. 类型擦除引发的冲突

public class NameClash<T> {        public boolean equals(T value) {                 return false ;       }  }  
从这个类的定义中来看,存在两个equals方法,一个是自身定义的public boolean equals(T value) {...},一个是从Object继承的public boolean equals(Object obj) {...},但类型擦除以后,前者方法成为了public boolean equals(Object value) {...},而在一个类中同时存在两个方法名和参数一样的方法是不可能的,所以这里引发的冲突是没法通过编译器的。可以通过重新命名方法进行修正。

擦除引起的冲突还体现在另一点上,再看一段错误的代码:

class Calendar implements Comparable<Calendar> {...}  class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar> {...}  

 
上述代码是非法的,为什么?回顾一下类型擦除后,虚拟机会为Calendar 类合成桥方法,实现了Comparable<Calendar>获得一个桥方法:
      public int compareTo (Object o) {return compareTo((Calendar)o);}
      而实现了Comparable<GregorianCalendar >在类型擦除后,虚拟机为GregorianCalendar合成一个桥方法:     
      public int compareTo (Object o) {return compareTo((GregorianCalendar )o);}
      这样一来在GregorianCalendar类中存在两个一样的方法,这是不允许的。

原创粉丝点击