Documentation

The Java™ Tutorials
Hide TOC
Type Inference类型推断
Trail: Learning the Java Language
Lesson: Generics (Updated)

Type Inference类型推断

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>());

Type Inference and Generic Methods类型推断和泛型方法

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 Integer:或者,如果省略类型见证,Java编译器会(根据方法的参数)自动推断类型参数为Integer

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

Type Inference and Instantiation of Generic Classes泛型类的类型推断和实例化

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

Type Inference and Generic Constructors of Generic and Non-Generic Classes泛型类和非泛型类的类型推断和泛型构造函数

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的类型IntegerNote that the constructor for this generic class contains a formal type parameter, T. 注意,这个泛型类的构造函数包含一个形式类型参数TThe 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推断类型IntegerIt infers the type String for the formal type parameter, T, of the constructor of this generic class.它为这个泛型类的构造函数的形式类型参数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. 推理算法不使用程序后面的结果。

Target Types目标类型

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的值为StringThus, in Java SE 8, the following statement compiles:因此,在Java SE 8中,以下语句能正常编译:

processStringList(Collections.emptyList());

See Target Typing in Lambda Expressions for more information.有关详细信息,请参阅在Lambda表达式中的目标类型


Previous page: Generics, Inheritance, and Subtypes
Next page: Wildcards