# 类型推断 > 原文: [https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html](https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html) _ 类型推断*是 Java 编译器查看每个方法调用和相应声明以确定使调用适用的类型参数(或参数)的能力。推理算法确定参数的类型,如果可用,还确定分配或返回结果的类型。最后,推理算法试图找到适用于所有参数的*最具体*类型。 为了说明最后一点,在下面的例子中,推断确定传递给`pick`方法的第二个参数是`Serializable`类型: ```java static T pick(T a1, T a2) { return a2; } Serializable s = pick("d", new ArrayList()); ``` [Generic Methods](methods.html) 向您介绍了类型推断,它使您能够像普通方法一样调用泛型方法,而无需在尖括号之间指定类型。考虑以下示例, [`BoxDemo`](examples/BoxDemo.java) ,它需要 [`Box`](examples/Box.java) 类: ```java public class BoxDemo { public static void addBox(U u, java.util.List> boxes) { Box box = new Box<>(); box.set(u); boxes.add(box); } public static void outputBoxes(java.util.List> boxes) { int counter = 0; for (Box box: boxes) { U boxContents = box.get(); System.out.println("Box #" + counter + " contains [" + boxContents.toString() + "]"); counter++; } } public static void main(String[] args) { java.util.ArrayList> listOfIntegerBoxes = new java.util.ArrayList<>(); BoxDemo.addBox(Integer.valueOf(10), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes); BoxDemo.outputBoxes(listOfIntegerBoxes); } } ``` 以下是此示例的输出: ```java Box #0 contains [10] Box #1 contains [20] Box #2 contains [30] ``` 泛型方法`addBox`定义了一个名为`U`的类型参数。通常,Java 编译器可以推断泛型方法调用的类型参数。因此,在大多数情况下,您不必指定它们。例如,要调用泛型方法`addBox`,可以使用*类型见证*指定类型参数,如下所示: ```java BoxDemo.addBox(Integer.valueOf(10), listOfIntegerBoxes); ``` 或者,如果省略类型见证,Java 编译器会自动推断(从方法的参数)类型参数是`Integer`: ```java BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes); ``` 只要编译器可以从上下文中推断出类型参数,就可以用一组空的类型参数(`<>`)替换调用泛型类的构造器所需的类型参数。这对尖括号被非正式称为[钻石](types.html#diamond)。 例如,请考虑以下变量声明: ```java Map> myMap = new HashMap>(); ``` 您可以使用一组空类型参数(`<>` )替换构造器的参数化类型: ```java Map> myMap = new HashMap<>(); ``` 请注意,要在泛型类实例化期间利用类型推断,必须使用菱形。在以下示例中,编译器生成未经检查的转换警告,因为`HashMap()`构造器引用`HashMap`原始类型,而不是`Map<String, List<String>>`类型: ```java Map> myMap = new HashMap(); // unchecked conversion warning ``` 请注意,构造器在泛型和非泛型类中都可以是通用的(换句话说,声明它们自己的形式类型参数)。请考虑以下示例: ```java class MyClass { MyClass(T t) { // ... } } ``` 考虑以下类`MyClass`的实例化: ```java new MyClass("") ``` 该语句创建参数化类型`MyClass<Integer>`的实例;该语句显式指定泛型类`MyClass<X>`的形式类型参数`X`的类型`Integer`。请注意,此泛型类的构造器包含形式类型参数`T`。编译器推断出此泛型类的构造器的形式类型参数`T`的类型`String`(因为此构造器的实际参数是`String`对象)。 Java SE 7 之前版本的编译器能够推断通用构造器的实际类型参数,类似于泛型方法。但是,如果使用菱形(`<>`),Java SE 7 及更高版本中的编译器可以推断正在实例化的泛型类的实际类型参数。请考虑以下示例: ```java MyClass myObject = new MyClass<>(""); ``` 在此示例中,编译器推断出泛型类`MyClass<X>`的形式类型参数`X`的类型`Integer`。它推断出这个泛型类的构造器的形式类型参数`T`的类型`String`。 * * * **Note:** It is important to note that the inference algorithm uses only invocation arguments, target types, and possibly an obvious expected return type to infer types. The inference algorithm does not use results from later in the program. * * * Java 编译器利用目标类型来推断泛型方法调用的类型参数。表达式的*目标类型*是 Java 编译器所期望的数据类型,具体取决于表达式的显示位置。考虑方法`Collections.emptyList`,声明如下: ```java static List emptyList(); ``` 请考虑以下赋值语句: ```java List listOne = Collections.emptyList(); ``` 这个陈述期待`List<String>`的一个例子;此数据类型是目标类型。因为方法`emptyList`返回类型`List<T>`的值,编译器会推断类型参数`T`必须是值`String`。这适用于 Java SE 7 和 8.或者,您可以使用类型见证并指定`T`的值,如下所示: ```java List listOne = Collections.emptyList(); ``` 但是,在这种情况下,这不是必需的。不过,在其他情况下这是必要的。请考虑以下方法: ```java void processStringList(List stringList) { // process stringList } ``` 假设您要使用空列表调用方法`processStringList`。在 Java SE 7 中,以下语句不编译: ```java processStringList(Collections.emptyList()); ``` Java SE 7 编译器生成类似于以下内容的错误消息: ```java List cannot be converted to List ``` 编译器需要类型参数`T`的值,因此它以值`Object`开头。因此,`Collections.emptyList`的调用返回类型`List<Object>`的值,这与方法`processStringList`不兼容。因此,在 Java SE 7 中,您必须指定 type 参数值的值,如下所示: ```java processStringList(Collections.emptyList()); ``` 在 Java SE 8 中不再需要这样。目标类型的概念已经扩展为包括方法参数,例如方法`processStringList`的参数。在这种情况下,`processStringList`需要`List<String>`类型的参数。方法`Collections.emptyList`返回值`List<T>`,因此使用目标类型`List<String>`,编译器推断类型参数`T`的值为`String`。因此,在 Java SE 8 中,以下语句编译: ```java processStringList(Collections.emptyList()); ``` 有关详细信息,请参阅 [Lambda 表达式](../../java/javaOO/lambdaexpressions.html)中的[目标类型](../../java/javaOO/lambdaexpressions.html#target-typing)。