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发行说明。
Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. 类型推断是Java编译器查看每个方法调用和相应声明的能力,以确定使调用适用的类型参数。The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. 推理算法确定参数的类型以及(如果可用)分配或返回结果的类型。Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.最后,推理算法试图找到适用于所有参数的最具体类型。
To illustrate this last point, in the following example, inference determines that the second argument being passed to the pick method is of type Serializable:为了说明最后一点,在下面的示例中,推理确定传递给pick方法的第二个参数的类型为Serializable:
static <T> T pick(T a1, T a2) { return a2; } Serializable s = pick("d", new ArrayList<String>());
Generic Methods introduced you to type inference, which enables you to invoke a generic method as you would an ordinary method, without specifying a type between angle brackets. 泛型方法向您介绍了类型推断,它使您能够像调用普通方法一样调用泛型方法,而无需在尖括号之间指定类型。Consider the following example, 考虑下面的例子,BoxDemo
, which requires the Box
class:BoxDemo
,它需要Box
类:
public class BoxDemo { public static <U> void addBox(U u, java.util.List<Box<U>> boxes) { Box<U> box = new Box<>(); box.set(u); boxes.add(box); } public static <U> void outputBoxes(java.util.List<Box<U>> boxes) { int counter = 0; for (Box<U> box: boxes) { U boxContents = box.get(); System.out.println("Box #" + counter + " contains [" + boxContents.toString() + "]"); counter++; } } public static void main(String[] args) { java.util.ArrayList<Box<Integer>> listOfIntegerBoxes = new java.util.ArrayList<>(); BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes); BoxDemo.outputBoxes(listOfIntegerBoxes); } }
The following is the output from this example:以下是此示例的输出:
Box #0 contains [10] Box #1 contains [20] Box #2 contains [30]
The generic method 泛型方法addBox
defines one type parameter named U
. addBox
定义了一个名为U
的类型参数。Generally, a Java compiler can infer the type parameters of a generic method call. 通常,Java编译器可以推断泛型方法调用的类型参数。Consequently, in most cases, you do not have to specify them. 因此,在大多数情况下,您不必指定它们。For example, to invoke the generic method 例如,要调用泛型方法addBox
, you can specify the type parameter with a type witness as follows:addBox
,可以使用类型见证指定类型参数,如下所示:
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
Alternatively, if you omit the type witness,a Java compiler automatically infers (from the method's arguments) that the type parameter is 或者,如果省略类型见证,Java编译器会(根据方法的参数)自动推断类型参数为Integer
:Integer
:
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (您可以将调用泛型类的构造函数所需的类型参数替换为一组空的类型参数(<>
) as long as the compiler can infer the type arguments from the context. <>
)只要编译器可以从上下文推断类型参数。This pair of angle brackets is informally called the diamond.这对尖括号非正式地称为钻石。
For example, consider the following variable declaration:例如,考虑下面的变量声明:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
You can substitute the parameterized type of the constructor with an empty set of type parameters (<>):您可以用一组空的类型参数替换构造函数的参数化类型(<>
):
Map<String, List<String>> myMap = new HashMap<>();
Note that to take advantage of type inference during generic class instantiation, you must use the diamond. 请注意,要在泛型类实例化期间利用类型推断,必须使用钻石。In the following example, the compiler generates an unchecked conversion warning because the 在下面的示例中,编译器生成未经检查的转换警告,因为HashMap()
constructor refers to the HashMap
raw type, not the Map<String, List<String>>
type:HashMap()
构造函数引用的是HashMap
原始类型,而不是Map<String, List<String>>
类型:
Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning
Note that constructors can be generic (in other words, declare their own formal type parameters) in both generic and non-generic classes. 注意,构造函数在泛型类和非泛型类中都可以是泛型的(换句话说,声明它们自己的形式类型参数)。Consider the following example:考虑下面的例子:
class MyClass<X> { <T> MyClass(T t) { // ... } }
Consider the following instantiation of the class 考虑下面的MyClass
:MyClass
类实例化:
new MyClass<Integer>("")
This statement creates an instance of the parameterized type 此语句创建参数化类型MyClass<Integer>
; the statement explicitly specifies the type Integer
for the formal type parameter, X
, of the generic class MyClass<X>
. MyClass<Integer>
的实例;该语句显式指定泛型类MyClass<X>
的形式类型参数X
的类型Integer
。Note that the constructor for this generic class contains a formal type parameter, 注意,这个泛型类的构造函数包含一个形式类型参数T
. T
。The compiler infers the type 编译器为该泛型类的构造函数的形式类型参数String
for the formal type parameter, T
, of the constructor of this generic class (because the actual parameter of this constructor is a String
object).T
推断类型String
(因为该构造函数的实际参数是String
对象)。
Compilers from releases prior to Java SE 7 are able to infer the actual type parameters of generic constructors, similar to generic methods. Java SE 7之前版本的编译器能够推断泛型构造函数的实际类型参数,类似于泛型方法。However, compilers in Java SE 7 and later can infer the actual type parameters of the generic class being instantiated if you use the diamond (但是,如果使用钻石(<>
). <>
),Java SE 7和更高版本中的编译器可以推断被实例化的泛型类的实际类型参数。Consider the following example:考虑下面的例子:
MyClass<Integer> myObject = new MyClass<>("");
In this example, the compiler infers the type 在本例中,编译器为泛型类Integer
for the formal type parameter, X
, of the generic class MyClass<X>
. MyClass<X>
的形式类型参数X
推断类型Integer
。It infers the type 它为这个泛型类的构造函数的形式类型参数String
for the formal type parameter, T
, of the constructor of this generic class.T
推断类型String
。
The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. Java编译器利用目标类型来推断泛型方法调用的类型参数。The target type of an expression is the data type that the Java compiler expects depending on where the expression appears. 表达式的目标类型是Java编译器期望的数据类型,具体取决于表达式出现的位置。Consider the method 考虑方法Collections.emptyList
, which is declared as follows:Collections.emptyList
,其声明如下:
static <T> List<T> emptyList();
Consider the following assignment statement:考虑下面的赋值语句:
List<String> listOne = Collections.emptyList();
This statement is expecting an instance of 此语句需要List<String>
; this data type is the target type. List<String>
的实例;此数据类型是目标类型。Because the method 因为方法emptyList
returns a value of type List<T>
, the compiler infers that the type argument T
must be the value String
. emptyList
返回的值类型为List<T>
,编译器推断类型参数T
必须是值字符串。This works in both Java SE 7 and 8. 这在Java SE 7和Java SE 8中都适用。Alternatively, you could use a type witness and specify the value of 或者,您可以使用类型见证并按如下方式指定T
as follows:T
的值:
List<String> listOne = Collections.<String>emptyList();
However, this is not necessary in this context. 然而,在这种情况下,这是不必要的。It was necessary in other contexts, though. 不过,在其他情况下,这是必要的。Consider the following method:考虑以下方法:
void processStringList(List<String> stringList) { // process stringList }
Suppose you want to invoke the method 假设您希望使用空列表调用processStringList
with an empty list. processStringList
方法。In Java SE 7, the following statement does not compile:在Java SE 7中,以下语句不编译:
processStringList(Collections.emptyList());
The Java SE 7 compiler generates an error message similar to the following:Java SE 7编译器生成类似以下内容的错误消息:
List<Object> cannot be converted to List<String>
The compiler requires a value for the type argument 编译器需要类型参数T
so it starts with the value Object
. T
的值,因此它以值Object
开始。Consequently, the invocation of 因此,对Collections.emptyList
returns a value of type List<Object>
, which is incompatible with the method processStringList
. Collections.emptyList
的调用将返回一个List<Object>
类型的值,它与processStringList
方法不兼容。Thus, in Java SE 7, you must specify the value of the value of the type argument as follows:因此,在Java SE 7中,必须按如下方式指定类型参数的值:
processStringList(Collections.<String>emptyList());
This is no longer necessary in Java SE 8. 这在Java SE 8中不再是必需的。The notion of what is a target type has been expanded to include method arguments, such as the argument to the method 什么是目标类型的概念已经扩展到包括方法参数,例如方法processStringList
. processStringList
的参数。In this case, 在这种情况下,processStringList
requires an argument of type List<String>
. processStringList
需要一个类型为List<String>
的参数。The method 方法Collections.emptyList
returns a value of List<T>
, so using the target type of List<String>
, the compiler infers that the type argument T
has a value of String
. Collections.emptyList
返回一个值List<T>
,因此,使用List<String>
的目标类型,编译器推断类型参数T
的值为String
。Thus, in Java SE 8, the following statement compiles:因此,在Java SE 8中,以下语句能正常编译:
processStringList(Collections.emptyList());
See Target Typing in Lambda Expressions for more information.有关详细信息,请参阅在Lambda表达式中的目标类型。