Documentation

The Java™ Tutorials
Hide TOC
Restrictions on Generics对泛型的限制
Trail: Learning the Java Language
Lesson: Generics (Updated)

Restrictions on Generics对泛型的限制

To use Java generics effectively, you must consider the following restrictions:要有效使用java泛型,必须考虑以下限制:

Cannot Instantiate Generic Types with Primitive Types无法用基元类型实例化泛型类型

Consider the following parameterized type:考虑以下参数化类型:

class Pair<K, V> {

    private K key;
    private V value;

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

    // ...
}

When creating a Pair object, you cannot substitute a primitive type for the type parameter K or V:创建Pair对象时,不能用基元类型替换类型参数KV

Pair<int, char> p = new Pair<>(8, 'a');  // compile-time error

You can substitute only non-primitive types for the type parameters K and V:只能用非基本类型替换类型参数KV

Pair<Integer, Character> p = new Pair<>(8, 'a');

Note that the Java compiler autoboxes 8 to Integer.valueOf(8) and 'a' to Character('a'):请注意,Java编译器将8自动装箱到Integer.valueOf(8),将'a'自动装箱到Character('a')

Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), new Character('a'));

For more information on autoboxing, see Autoboxing and Unboxing in the Numbers and Strings lesson.有关自动装箱的详细信息,请参阅数字和字符串课程中的自动装箱和取消装箱

Cannot Create Instances of Type Parameters无法创建类型参数的实例

You cannot create an instance of a type parameter. 不能创建类型参数的实例。For example, the following code causes a compile-time error:例如,以下代码导致编译时错误:

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

As a workaround, you can create an object of a type parameter through reflection:作为一种解决方法,您可以通过反射创建类型参数的对象:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

You can invoke the append method as follows:您可以按如下方式调用append方法:

List<String> ls = new ArrayList<>();
append(ls, String.class);

Cannot Declare Static Fields Whose Types are Type Parameters无法声明类型为类型参数的静态字段

A class's static field is a class-level variable shared by all non-static objects of the class. 类的静态字段是由类的所有非静态对象共享的类级变量。Hence, static fields of type parameters are not allowed. 因此,类型参数的静态字段是不允许的。Consider the following class:考虑下面的类:

public class MobileDevice<T> {
    private static T os;

    // ...
}

If static fields of type parameters were allowed, then the following code would be confused:如果允许类型参数的静态字段,则会混淆以下代码:

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

Because the static field os is shared by phone, pager, and pc, what is the actual type of os? 因为phonepagerpc共享静态域os,所以实际的os类型是什么?It cannot be Smartphone, Pager, and TabletPC at the same time. 它不能同时是SmartphonePagerTabletPCYou cannot, therefore, create static fields of type parameters.因此,不能创建类型参数的静态字段。

Cannot Use Casts or instanceof with Parameterized Types无法对参数化类型使用强制转换或instanceof

Because the Java compiler erases all type parameters in generic code, you cannot verify which parameterized type for a generic type is being used at runtime:由于Java编译器会擦除泛型代码中的所有类型参数,因此您无法验证在运行时使用泛型类型的哪个参数化类型:

public static <E> void rtti(List<E> list) {
    if (list instanceof ArrayList<Integer>) {  // compile-time error
        // ...
    }
}

The set of parameterized types passed to the rtti method is:传递给rtti方法的参数化类型集为:

S = { ArrayList<Integer>, ArrayList<String> LinkedList<Character>, ... }

The runtime does not keep track of type parameters, so it cannot tell the difference between an ArrayList<Integer> and an ArrayList<String>. 运行时不跟踪类型参数,因此无法区分ArrayList<Integer>ArrayList<String>The most you can do is to use an unbounded wildcard to verify that the list is an ArrayList:您最多只能使用无界通配符验证列表是否为ArrayList

public static void rtti(List<?> list) {
    if (list instanceof ArrayList<?>) {  // OK; instanceof requires a reifiable type
        // ...
    }
}

Typically, you cannot cast to a parameterized type unless it is parameterized by unbounded wildcards. 通常,除非参数化类型由无界通配符参数化,否则无法强制转换为参数化类型。For example:例如:

List<Integer> li = new ArrayList<>();
List<Number> ln = (List<Number>) li;  // compile-time error

However, in some cases the compiler knows that a type parameter is always valid and allows the cast. 但是,在某些情况下,编译器知道类型参数始终有效,并允许强制转换。For example:例如:

List<String> l1 = ...;
ArrayList<String> l2 = (ArrayList<String>)l1;  // OK

Cannot Create Arrays of Parameterized Types无法创建参数化类型的数组

You cannot create arrays of parameterized types. 不能创建参数化类型的数组。For example, the following code does not compile:例如,以下代码不编译:

List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error

The following code illustrates what happens when different types are inserted into an array:以下代码说明了在数组中插入不同类型时发生的情况:

Object[] strings = new String[2];
strings[0] = "hi";   // OK
strings[1] = 100;    // An ArrayStoreException is thrown.

If you try the same thing with a generic list, there would be a problem:如果对通用列表尝试相同的操作,则会出现问题:

Object[] stringLists = new List<String>[2];  // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>();   // OK
stringLists[1] = new ArrayList<Integer>();  // An ArrayStoreException should be thrown,
                                            // but the runtime can't detect it.

If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.如果允许参数化列表的数组,那么前面的代码将无法抛出所需的ArrayStoreException

Cannot Create, Catch, or Throw Objects of Parameterized Types无法创建、捕获或抛出参数化类型的对象

A generic class cannot extend the Throwable class directly or indirectly. 泛型类不能直接或间接扩展Throwable类。For example, the following classes will not compile:例如,以下类将不会编译:

// Extends Throwable indirectly
class MathException<T> extends Exception { /* ... */ }    // compile-time error

// Extends Throwable directly
class QueueFullException<T> extends Throwable { /* ... */ // compile-time error

A method cannot catch an instance of a type parameter:方法无法捕获类型参数的实例:

public static <T extends Exception, J> void execute(List<J> jobs) {
    try {
        for (J job : jobs)
            // ...
    } catch (T e) {   // compile-time error
        // ...
    }
}

You can, however, use a type parameter in a throws clause:但是,您可以在throws子句中使用类型参数:

class Parser<T extends Exception> {
    public void parse(File file) throws T {     // OK
        // ...
    }
}

Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type无法重载每个重载的形式参数类型擦除为相同原始类型的方法

A class cannot have two overloaded methods that will have the same signature after type erasure.一个类不能有两个重载方法,它们在类型擦除后将具有相同的签名。

public class Example {
    public void print(Set<String> strSet) { }
    public void print(Set<Integer> intSet) { }
}

The overloads would all share the same classfile representation and will generate a compile-time error.重载将共享相同的类文件表示,并将生成编译时错误。


Previous page: Non-Reifiable Types
Next page: Questions and Exercises: Generics