泛型的约束与局限性

因为Java并没有真正实现泛型,是编译器在编译的时候在字节码上做了手脚,这种实现理念造成Java泛型本身有很多漏洞。为了规避这些问题Java对泛型的使用上做了一些约束,但不可避免的还是有一些问题存在。这其中大多数限制都是由类型擦除引起的。
1.不能用类型参数替换基本类型:
   其原因是类型擦除,擦除类型后原先的类型参数被Object或者限定类型替换,而基本类型是不能被对象所存储的。可以使用它们的包装类。
 
2.运行时类型检查只是针对泛型类的原始类型进行的。getClass()方法只返回原始类型,因为JVM根本就不知道泛型这回事,它只知道原始类型。
   在《Java Core》中提到的
   Pair a = new Pair();
   a instanceof Pair<String>为真的问题在JDK1.6上面已经不存在,这样的语句是通不过检查的,产生一个"instacneof 泛型类型不合法"的错误。
 
3.异常。不能抛出也不能捕获泛型类的对象,使用泛型类来拓展Throwable也是非法的。
   public class GenericException <T> extends Exception{
            //泛型类无法继承Throwable,非法
   }
   不能在catch子句中使用类型变量。例如,下面的方法将不能编译:
   public static <T extends Throwable> void doWork(Class<T> t){
            try{
                    ...
            }catch(T e){//error --- can't catch type variable
                    ...
           }
   }
   但是,在异常声明中可以使用类型变量。下面这个方法是合法的:
   public static <T extends Throwable> void doWork(T t) throws T{//ok
            try{
                    ...
           }catch(Throwable realCause){
                    t.initCause(realCause);
                   throw t;
           }
   }
 
4.不能声明参数化类型的数组。原因还是类型擦除。
Pair<String>[] p = new Pair<String>[100];//这个是非法的,不能创建泛型数组
如果这样做合法的话,就会引发一个严重问题:
类型擦除之后,p的类型为Pair[],将其转换为Object[]是合法的
Object[] o = p;
数组可以记住它的元素类型,如果试图存入一个错误类型的元素,就会抛出一个ArrayStoreException异常,但是
o[0] = new Pair<Date>();
是可以通过数组存储的检测,因为经过类型擦除Pair<Date>和Pair<String>是相同的类型,但是在取数据的时候,比如o[0].getFirst(),我们知道编译器在字节码中加入了类型强制转换的指令,即(String)o[0].getFirst(),这时候类型错误就会发生,但这之前的一系列操作看起来又都是合法的。Java语言的严密性受到挑战。(如果参数化类型数组像列子那样使用,会导致运行时类型不匹配的错误,而在java中这种错误应该在编译期发现,所以java禁止使用参数化类型数组)
 
5.泛型类型的实例化。
不能实例化泛型类型。例如,下面Pair<T>构造器是非法的:
public Pair(){ first = new T(); second = new T(); }
类型擦除将变量T替换成Object,但这段代码的本意肯定不是调用new Object()。
类似地,不能建立一个泛型数组:
public <T> T[] minmax(T[] a){ T[] mm = new T[2]; ... }
类型擦除会让这个方法总是构造一个Object[2]数组。
但是,通过调用Class.newInstance和Array.newInstance方法,可以利用反射构造泛型对象和数组。
 
6.静态上下文。不能在静态域或者静态方法中引用类型变量。见如下代码:
class C<T> { static T member; C(T t) { member = t; } T getMember() { return member; } public static void main(String[] args) {     C<String> c = new C<String>("test");//1     System.out.println(c.getMember().toString());     new C<Integer>(new Integer(1));//2     System.out.println(c.getMember().toString()); } }
静态域和静态方法是共享的。C中的静态域member到底是什么类型的哪?对程序而言这是个潜在的安全隐患,因为在执行语句2的时候会发生 ClassCastException异常,因为编译器在获取域的时候加入了强制类型转换,不能要求原来是String类型的域,现在引用一个Integer对象。
 
7.类型擦除后的冲突。例如:
public class B<T>{
        public void fun(T t){ ... }//方法1
       public void fun(Object t){ ... }//方法2
}
这两个方法实际上是冲突的,类型擦除后它们具有相同的签名。补救的办法只能是重新命名。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值