`
trophy
  • 浏览: 176566 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

java 范型 约束

    博客分类:
  • java
阅读更多

 

转自:

http://www.myexception.cn/mobile/692129.html

向作者致谢!

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

大多数这些需要注意的问题,都是由类型擦除引起的。

一、基本类型

不能用类型参数替换基本类型。就比如,没有ArrayList<double>,只有ArrayList<Double>。因为当类型擦除后,ArrayList的原始类型变为Object,但是Object类型不能存储double值,只能引用Double的值。

 

二、运行时类型查新

举个例子:

 

ArrayList<String> arrayList=new ArrayList<String>();

因为类型擦除之后,ArrayList<String>只剩下原始类型,泛型信息String不存在了。

 

那么,运行时进行类型查询的时候使用下面的方法是错误的

 

if( arrayList instanceof ArrayList<String>)

java限定了这种类型查询的方式

 

 

if( arrayList instanceof ArrayList<?>)



三、异常

1、不能抛出也不能捕获泛型类的对象。事实上,泛型类扩展Throwable都不合法。例如:下面的定义将不会通过编译:

 

public class Problem<T> extends Exception{......}

为什么不能扩展Throwable,因为异常都是在运行时捕获和抛出的,而在编译的时候,泛型信息全都会被擦除掉,那么,假设上面的编译可行,那么,在看下面的定义:

 

 

try{
}catch(Problem<Integer> e1){
。。
}catch(Problem<Number> e2){
...
} 

类型信息被擦除后,那么两个地方的catch都变为原始类型Object,那么也就是说,这两个地方的catch变的一模一样,就相当于下面的这样

 

 

try{
}catch(Problem<Object> e1){
。。
}catch(Problem<Object> e2){
...

这个当然就是不行的。就好比,catch两个一模一样的普通异常,不能通过编译一样:

 

 

try{
}catch(Exception e1){
。。
}catch(Exception  e2){//编译错误
...

 

2、不能再catch子句中使用泛型变量

 

public static <T extends Throwable> void doWork(Class<T> t){
        try{
            ...
        }catch(T e){ //编译错误
            ...
        }
   }

因为泛型信息在编译的时候已经变味原始类型,也就是说上面的T会变为原始类型Throwable,那么如果可以再catch子句中使用泛型变量,那么,下面的定义呢:

 

 

public static <T extends Throwable> void doWork(Class<T> t){
        try{
            ...
        }catch(T e){ //编译错误
            ...
        }catch(IndexOutOfBounds e){
        }                         
 }

,根据异常捕获的原则,一定是子类在前面,父类在后面,那么上面就违背了这个原则。即使你在使用该静态方法的使用T是ArrayIndexOutofBounds,在编译之后还是会变成Throwable,ArrayIndexOutofBounds是IndexOutofBounds的子类,违背了异常捕获的原则。所以java为了避免这样的情况,禁止在catch子句中使用泛型变量。

 

 

但是在异常声明中可以使用类型变量。下面方法是合法的。

 

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

上面的这样使用是没问题的。

 

 

四、数组

不能声明参数化类型的数组。如:

  Pair<String>[] table = newPair<String>(10); //ERROR


   这是因为擦除后,table的类型变为Pair[],可以转化成一个Object[]。
  

Object[] objarray =table;

  数组可以记住自己的元素类型,下面的赋值会抛出一个ArrayStoreException异常。
   

objarray ="Hello"; //ERROR

  对于泛型而言,擦除降低了这个机制的效率。下面的赋值可以通过数组存储的检测,但仍然会导致类型错误。  

 objarray =new Pair<Employee>();

 

提示:如果需要收集参数化类型对象,直接使用ArrayList:ArrayList<Pair<String>>最安全且有效。

 

五、泛型类型的实例化 

不能使用像new T(...),new T[...],T.class这样的表达式

不能实例化泛型类型。如,

 

    first = newT(); //ERROR


   是错误的,类型擦除会使这个操作做成new Object()。

   可以使用Class.newInstance()方法:

    cl.newInstanc();
   不能建立一个泛型数组。
  

  public<T> T[] minMax(T[] a){
       T[] mm = new T[2]; //ERROR
       ...
  }


   类似的,擦除会使这个方法总是构靠一个Object[2]数组。但是,可以用反射构造泛型对象和数组。
   利用反射,调用Array.newInstance:

 

 publicstatic <T extends Comparable> T[]minmax(T[] a)

    {

       T[] mm == (T[])Array.newInstance(a.getClass().getComponentType(),2);

        ...

       // 以替换掉以下代码

       // Obeject[] mm = new Object[2];

       // return (T[]) mm;

    }


 

六、静态上下文

  不能在静态域或者方法中引用类型变量。

 

public class Test<M> {

public static void test(M m) {  //error

}
} 

这句话的错误提示是无法在静态上下文中引用非静态类 M.
因为test方法是静态的可以用类名直接调用,而M只有在类Test创建对象的时候才会明确.
test静态方法存在的时候还没明确M是什么.
所以静态方法不可以访问类上定义的泛型.
如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上.
可以改成:

 

public class Test {

public static <M> void test(M m) {  //right

}
} 

 

 

 
  七、类型擦除后的冲突

1、

当泛型类型被擦除后,创建条件不能产生冲突。如果在Pair类中添加下面的equals方法:

 

class Pair<T>   {
	public boolean equals(T value) {
		return null;
	}
	
}

考虑一个Pair<String>。从概念上,它有两个equals方法:

 

booleanequals(String); //在Pair<T>中定义

boolean equals(Object); //从object中继承

但是,这只是一种错觉。实际上,擦除后方法

booleanequals(T)

变成了方法 booleanequals(Object)

这与Object.equals方法是冲突的!当然,补救的办法是重新命名引发错误的方法。

 

2、

泛型规范说明提及另一个原则“要支持擦除的转换,需要强行制一个类或者类型变量不能同时成为两个接口的子类,而这两个子类是同一接品的不同参数化。”

下面的代码是非法的:

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

GregorianCalendar会实现Comparable<Calender>和Compable<GregorianCalendar>,这是同一个接口的不同参数化实现。

 

这一限制与类型擦除的关系并不很明确。非泛型版本:

 

class Calendar implements Comparable{ ... }
class GregorianCalendar extends Calendar implements Comparable{...} //ERROR

是合法的。

有可能与合成的桥方法产生冲突有关:

public int compareTo(Object other) {return compareTo((X) other);}

方法体产生冲突,X应是Calender还是GregorianCalendar,不确定

 

 

八、泛型中 参数化类型不考虑继承关系

 

ArrayList<String> arrayList1=new ArrayList<Object>();//编译错误
ArrayList<Object> arrayList1=new ArrayList<String>();//编译错误

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics