Restrictions on Generics

为了更有效的使用泛型,我们需要注意以下这些限制:

  • 无法使用基础类型实例化泛型类型

  • 不能创建类型参数的实例

  • 不能将静态字段的类型定义为类型参数

  • 不能对参数化类型使用Casts(强转)或者instanceof

  • 不能创建参数化类型的数组

  • 不能对参数化类型进行CreateCatch或者Throw

  • 如果重载函数的形参进行类型擦除以后为相同的原始类型,则无法对函数进行重栽

Cannot Instantiate Generic Types with Primitive Types

以下面的参数化类型为例:

class Pair<K, V> {

    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    // ...
}

在创建一个Pair对象时,我们不能使用基础类型来替换类型参数K或者V

我们只能将类型参数K或者V替换为非基础类型:

注意这里Java编译器会将8自动装箱(autoboxes)为Integer.valueOf(8)a自动装箱为Character('a')

更多关于自动装箱的内容,请参考Numbers and Strings章节中的Autoboxing and Unboxing

Cannot Create Instances of Type Parameters

我们不对使用类型参数创建实例。下面这段代码将会引起编译时报错:

变通的方式是,我们可以通过类型参数的反射来创建相应的对象:

我们可以像下面这段代码一样调用append函数:

Cannot Declare Static Fields Whose Types are Type Parameters

一个类中的静态字段是类级别的也就是被该类的所有非静态实例所共享的。因此,不允许有类型参数的静态字段。以下面的类为例:

如果允许存在类型参数的静态字段,那么下面这段代码是令人困惑的:

由于静态字段osphone, pager, pc共享的,那么os真正的类型是什么呢?它不可能同时是Smartphone, Pager, TabletPC。所以我们不能创建类型参数的静态字段。

Cannot Use Casts or instanceof with Parameterized Types

由于Java编译器会擦出所有泛型代码中的类型参数,所以在运行时,我们无法检验使用的是泛型类型的哪种参数化类型:

传入rtti函数的参数化类型的集合是:

运行时并不保留类型参数的痕迹,所以并不能区分ArrayList<Integer>ArrayList<String>。我们能做的很自由使用无边界通配去检验队列是否是ArrayList

通常,除非使用无边界通配对其进行参数化,否则无法将其强转为参数化类型。以下面代码为例:

然而,有些情况下编译器能识别类型参数,此时强转是允许并有效的。比如:

Cannot Create Arrays of Parameterized Types

我们不能创建参数化类型的数组,比如下面这段代码无法编译通过:

下面这段代码展示了将不同类型的数据插入同一个数组是会发生什么:

当我对泛型列表做同样的操作,就会产生问题:

如果允许使用参数化列表的数组,那么上面的代码将无法抛出应有的ArrayStoreException

Cannot Create, Catch, or Throw Objects of Parameterized Types

泛型类不可以直接或者间接的扩展Throwable类,下面的类无法编译通过:

函数中无法捕获类型参数的实例

然而,我们可以在throws条件中使用类型参数:

Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type

一个类不能具有两个在类型擦除后将具有相同的签名的重载函数

上面的重载将共享相同的类文件,所以将生成编译时报错

Last updated

Was this helpful?