The Java Tutorials have been written for JDK 8.Java教程是为JDK 8编写的。Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.本页中描述的示例和实践没有利用后续版本中引入的改进,并且可能使用不再可用的技术。See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.有关所有JDK版本的新功能、增强功能以及已删除或不推荐的选项的信息,请参阅JDK发行说明。
To use Java generics effectively, you must consider the following restrictions:要有效使用java泛型,必须考虑以下限制:
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对象时,不能用基元类型替换类型参数K或V:
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:只能用非基本类型替换类型参数K和V:
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.有关自动装箱的详细信息,请参阅数字和字符串课程中的自动装箱和取消装箱。
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);
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? 因为phone、pager和pc共享静态域os,所以实际的os类型是什么?It cannot be Smartphone, Pager, and TabletPC at the same time. 它不能同时是Smartphone、Pager和TabletPC。You cannot, therefore, create static fields of type parameters.因此,不能创建类型参数的静态字段。
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
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。
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 // ... } }
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.重载将共享相同的类文件表示,并将生成编译时错误。