# 类型推断

> 原文： [https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html](https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html)

_ 类型推断 _ 是 Java 编译器查看每个方法调用和相应声明以确定使调用适用的类型参数（或参数）的能力。推理算法确定参数的类型，如果可用，还确定分配或返回结果的类型。最后，推理算法试图找到适用于所有参数的 _ 最具体 _ 类型。

为了说明最后一点，在下面的例子中，推断确定传递给 `pick` 方法的第二个参数是 `Serializable` 类型：

```
static <T> T pick(T a1, T a2) { return a2; }
Serializable s = pick("d", new ArrayList<String>());

```

[Generic Methods](methods.html) 向您介绍了类型推断，它使您能够像普通方法一样调用泛型方法，而无需在尖括号之间指定类型。考虑以下示例， [`BoxDemo`](examples/BoxDemo.java) ，它需要 [`Box`](examples/Box.java) 类：

```
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);
  }
}

```

以下是此示例的输出：

```
Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]

```

泛型方法`addBox`定义了一个名为`U`的类型参数。通常，Java 编译器可以推断泛型方法调用的类型参数。因此，在大多数情况下，您不必指定它们。例如，要调用泛型方法`addBox`，可以使用 _ 类型见证 _ 指定类型参数，如下所示：

```
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);

```

或者，如果省略类型见证，Java 编译器会自动推断（从方法的参数）类型参数是`Integer`：

```
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);

```

只要编译器可以从上下文中推断出类型参数，就可以用一组空的类型参数（`&lt;&gt;`）替换调用泛型类的构造函数所需的类型参数。这对尖括号被非正式称为[钻石](types.html#diamond)。

例如，请考虑以下变量声明：

```
Map<String, List<String>> myMap = new HashMap<String, List<String>>();

```

您可以使用一组空类型参数（`&lt;&gt;` ）替换构造函数的参数化类型：

```
Map<String, List<String>> myMap = new HashMap<>();

```

请注意，要在泛型类实例化期间利用类型推断，必须使用菱形。在以下示例中，编译器生成未经检查的转换警告，因为`HashMap()`构造函数引用`HashMap`原始类型，而不是`Map&lt;String, List&lt;String&gt;&gt;`类型：

```
Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

```

请注意，构造函数在泛型和非泛型类中都可以是通用的（换句话说，声明它们自己的形式类型参数）。请考虑以下示例：

```
class MyClass<X> {
  <T> MyClass(T t) {
    // ...
  }
}

```

考虑以下类`MyClass`的实例化：

```
new MyClass<Integer>("")

```

该语句创建参数化类型`MyClass&lt;Integer&gt;`的实例;该语句显式指定泛型类`MyClass&lt;X&gt;`的形式类型参数`X`的类型`Integer`。请注意，此泛型类的构造函数包含形式类型参数`T`。编译器推断出此泛型类的构造函数的形式类型参数`T`的类型`String`（因为此构造函数的实际参数是`String`对象）。

Java SE 7 之前版本的编译器能够推断通用构造函数的实际类型参数，类似于泛型方法。但是，如果使用菱形（`&lt;&gt;`），Java SE 7 及更高版本中的编译器可以推断正在实例化的泛型类的实际类型参数。请考虑以下示例：

```
MyClass<Integer> myObject = new MyClass<>("");

```

在此示例中，编译器推断出泛型类`MyClass&lt;X&gt;`的形式类型参数`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`，声明如下：

```
static <T> List<T> emptyList();

```

请考虑以下赋值语句：

```
List<String> listOne = Collections.emptyList();

```

这个陈述期待`List&lt;String&gt;`的一个例子;此数据类型是目标类型。因为方法`emptyList`返回类型`List&lt;T&gt;`的值，编译器会推断类型参数`T`必须是值`String`。这适用于 Java SE 7 和 8.或者，您可以使用类型见证并指定`T`的值，如下所示：

```
List<String> listOne = Collections.<String>emptyList();

```

但是，在这种情况下，这不是必需的。不过，在其他情况下这是必要的。请考虑以下方法：

```
void processStringList(List<String> stringList) {
    // process stringList
}

```

假设您要使用空列表调用方法`processStringList`。在 Java SE 7 中，以下语句不编译：

```
processStringList(Collections.emptyList());

```

Java SE 7 编译器生成类似于以下内容的错误消息：

```
List<Object> cannot be converted to List<String>

```

编译器需要类型参数`T`的值，因此它以值`Object`开头。因此，`Collections.emptyList`的调用返回类型`List&lt;Object&gt;`的值，这与方法`processStringList`不兼容。因此，在 Java SE 7 中，您必须指定 type 参数值的值，如下所示：

```
processStringList(Collections.<String>emptyList());

```

在 Java SE 8 中不再需要这样。目标类型的概念已经扩展为包括方法参数，例如方法`processStringList`的参数。在这种情况下，`processStringList`需要`List&lt;String&gt;`类型的参数。方法`Collections.emptyList`返回值`List&lt;T&gt;`，因此使用目标类型`List&lt;String&gt;`，编译器推断类型参数`T`的值为`String`。因此，在 Java SE 8 中，以下语句编译：

```
processStringList(Collections.emptyList());

```

有关详细信息，请参阅 [Lambda 表达式](../../java/javaOO/lambdaexpressions.html)中的[目标类型](../../java/javaOO/lambdaexpressions.html#target-typing)。