The Java™ Tutorials — Generics :Type Inference 类型推断

The Java™ Tutorials — Generics :Type Inference 类型推断

原文地址:https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

关键点

  • 理解编译器是如何利用目标类型来推算泛型变量的值
  • 注意下面的代码:在Java7中无法编译通过,而在Java8中却可以。Java8的编译器可以通过方法形参类型对泛型变量进行推断
static <T> List<T> emptyList();
void processStringList(List<String> stringList) {
// process stringList
}
processStringList(Collections.emptyList());

全文翻译

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. 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.

Java编译器有一种类型推断的能力。它会查看所有的方法调用,以及相关联的声明,从而推断出使调用有效的一个或多个类型参数。此推断算法决定了参数的类型。并在可行时,推断出结果被赋予的或是返回的类型。最终,此算法会尝试找出所有参数的最适用的类型。

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. Generally, a Java compiler can infer the type parameters of a generic method call. 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()定义了一个名为U的类型参数。总的来说,一个Java编译器可以推断出泛型调用中的类型参数。因此,在大部分情况下,你无需显示地指明它们。例如,为了调用泛型方法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 theMap<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 typeMyClass<Integer>; the statement explicitly specifies the type Integer for the formal type parameter, X, of the generic class MyClass<X>. Note that the constructor for this generic class contains a formal type parameter, 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).

这个语句创建了一个MyClass<Integer>类型的实例。它为MyClass<X>的正式类型参数X显示地指明了类型是Integer。注意泛型类的构造器带有一个正式的类型参数T。编译器从这个参数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. 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 (<>). Consider the following example:

java SE 7 之前发布的编译器可以从泛型构造器中推断出实际类型参数,这与泛型方法类似。然而,如果你使用了钻石运算符,Java SE 7 和以后的编译器就可以从正在实例化的泛型类中推断出实际类型参数。看看下面的例子:

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>. It infers the type String for the formal type parameter, T, of the constructor of this generic class.

在此例中,编译器为泛型类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.

注意:推断算法为了推断类型,仅仅使用了调用的变量,目标类型,也许还使用了一个明显为预期的返回类型。算法没有使用程序后半段中的推断结果。

Target Types 目标类型

The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. The target type of an expression is the data type that the Java compiler expects depending on where the expression appears. Consider the method Collections.emptyList, which is declared as follows:

Java 编译器利用输入的目标类型,推测调用的泛型方法中的类型参数。所谓的一个表达式的目标类型就是一个数据类型。编译器希望能够在表达式出现的地方利用这个数据类型。看看这个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. Because the method emptyList returns a value of type List<T>, the compiler infers that the type argument T must be the value String. This works in both Java SE 7 and 8. Alternatively, you could use a type witness and specify the value of T as follows:

这个语句期望能获得一个List<String>的实例;这个数据类型就是目标类型。由于emptyList方法返回了一个List<T>类型的值,编译器推断这个类型变量T的值一定是String。这个算法可在Java SE 7 和 8 中同时工作。不过,你也可以使用一个类型见证来像下面这样明确地指定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. In Java SE 7, the following statement does not compile:

假设你想要通过一个空List来调用processStringList方法。在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. Consequently, the invocation of Collections.emptyList returns a value of type List<Object>, which is incompatible with the method processStringList. Thus, in Java SE 7, you must specify the value of the value of the type argument as follows:

编译器需要一个类型变量T的值,因此它就赋了一个初值Object。结果,Collections.emptyList返回了一个List<Object>类型,而这个类型与方法processStringList()并不兼容。因此,在Java SE 7 中,你必须像下面这样明确指明类型变量的值:

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

This is no longer necessary in 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. In this case, processStringList requires an argument of type 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. Thus, in Java SE 8, the following statement compiles:

这在Java SE 8 中并不是必须的。何为目标类型的概念已经被扩展,它包含了方法变量如processStringList()中的形参。在本例中,processStringList需要一个List<String>类型的参数。方法Collections.emptyList返回了一个List<T>类型的值,所以根据目标类型List<String>,编译器推断出类型变量T具有一个String的值。因此,在JavaSE 8 中,下面的语句是可以编译通过的:

processStringList(Collections.emptyList());

See Target Typing in Lambda Expressions for more information.

更多信息,参见《Lambda表达式》中《目标类型》章节。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值