Java编程思想之泛型(拓展)

来源:互联网 发布:modbus crc16校验算法 编辑:程序博客网 时间:2024/06/03 17:55

问题

在使用Java泛型时会出现的各类问题

1) 任何基本类型都不能作为类型参数

即ArrayList< int >之类的是不行的,不过使用用基本类型的包装器类以及Java SE5的自动包装机制就能解决。列如:创建ArrayList< Integer> ,并将基本类型int应用于这个容器,自动包装机制将自动地实现int到Integer的双向转换。
自动包装机制不能应用于数组,

public class AutoboxTest{    static <T> T[] get(T[] a){        System.out.println(a);        return a;    }    public static void main(String[] args){        get(new int[8]); //get(new Integer[8]);即可    }}/*编译结果:AutoboxTest.java:7: 错误: 无法将类 AutoboxTest中的方法 get应用到给定类型;                get(new int[8]);                ^  需要: T[]  找到: int[]  原因: 推论变量 T 具有不兼容的限制范围    等式约束条件: int    上限: Object  其中, T是类型变量:    T扩展已在方法 <T>get(T[])中声明的Object1 个错误*/

2)实现参数化接口

一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。

interface P<T>{}class P1 implements P<P1>{}public class InterfaceTest extends P1     implements P<InterfaceTest>{}/*编译结果:    错误: 无法使用以下不同的参数继承P: <InterfaceTest> 和 <P1>*/

如果从P的两种用法中都移除掉泛型参数(或者精确地相同,包括参数类型在内),这段代码就可以编译。

interface P<T>{}class P1 implements P{}public class InterfaceTest extends P1     implements P{}

3) 转型和警告

容器在内部将值存储为Object,并在获取这些值时,再将它们转型回T:

class Test<T>{  private Object c;  public void set(T i){ c = i;}  @SuppressWarnings("unchecked")  //不使用该句,会报Test.java使用了未经检查或不安全的操作。  public T get(){ return (T)c;}}

有时,你会被强制要求转型,但是又被告知不应该转型。为了解决这个问题,必须使用在Java SE5中引入的新的转型形式,即通过泛型类来转型:
List<类名> li = List.class.cast(in.readObject()); //in是一个ObjectInputStream对象
但是,不能转到实际类型,即不能声明:List<类名>.class.cast(in.readObject());

4) 重载

当被擦除的参数不能产生唯一的参数列表时,必须提供明显有区别的方法名。
void f(List<T> v){} void f(List<W> c){} //T、W是当前泛型类的类型变量,不能编译,应当使方法名不同。

自限定类型

class SelfBounded<T extends SelfBounded<T>>{ //...
SelfBounded类接受泛型参数T,而T由一个边界类限定,这个边界就是拥有T作为其参数的SelfBounded。

1) 古怪的循环泛型(CRG)

不能直接继承一个泛型参数,但是,可以继承在其自己的定义 中使用这个泛型参数的类。可以声明:

class GenericType<T>{}public class CuriouslyRecurringGeneric extends GenericType<CuriouslyRecurringGeneric>{}

我在创建一个新类,它继承自一个泛型类型,这个泛型类型接受我的类的名字作为其参数。
Java中的泛型关乎参数和返回类型,因此它能够产生使用导出类作为其参数和返回类型的基类,它还能将导出类型用作其域类型,甚至那些将被擦除为Object的类型。这就是CRG的本质:* 基类用导出类替代其参数*。 这意味着泛型基类变成了一种其所有导出类的公共功能的模版,但是这些功能对于其所有参数和返回值,将使用导出类型。也就是说,在所产生的类中将使用确切类型而不是基类型。

2) 自限定

自限定采取额外的步骤,强制泛型当作其自己的边界参数来使用。
自限定所做的,就是要求在继承关系中,像下面这样使用这个类:
calss A extends SelfBounded<A>{}
可以保证类型参数与正在被定义的类相同。自限定只能强制作用于继承关系。如果使用自限定,就应该了解这个类所用的类型参数将与使用这个参数的类具有相同的基类型。

3) 参数协变

自类型的价值在于它们可以产生协变参数类型——方法参数类型会随子类而变化。尽管自限定类型还可以产生与子类类型相同的返回类型。但是这并不重要,因为协变返回类型是在Java SE5中引入的。导出类方法应该能够返回比它覆盖的基类方法更具体的类型。
自限定泛型将产生确切的导出类作为其返回值。
如果不使用自限定,将重载参数类型。如果使用了自限定,只能获得某个方法的一个版本,它将接受确切的参数类型。