【java】java中 Arrays.asList返回的泛型类型如何确定?

最近在看java编程思想(第四版)的持有容器,书上11章223页代码如下

【java】java中 Arrays.asList返回的泛型类型如何确定?
在写这个示例的时候,我发现红框内的代码可以通过编译,和书上不一致?

package com.xunli.holding;

import java.util.*;

class Snow {}

class Powder extends Snow {}

class Light extends Powder {}

class Heavy extends Powder {}

class Crusty extends Snow {}

class Slush extends Snow {}

public class AppleAndOrangeWithnotGenerics {

public static void main(String[] args) {

List<Snow> snow1 = Arrays.asList(

new Crusty(), new Slush(), new Powder()

);

List<Snow> snow2 = Arrays.asList(

new Light(), new Heavy()

);

List<Snow> snow3 = new ArrayList<Snow>();

Collections.addAll(snow3, new Light(), new Heavy());

List<Snow> snow4 = Arrays.<Snow>asList(new Light(), new Heavy());

}

}

【java】java中 Arrays.asList返回的泛型类型如何确定?

这是为什么?

回答

从 Java7 开始,官方就着手增强 Java 对泛型类型的推导能力,对应的项目有 JDK7 的 JSR 334 (Project Coin) 和 JDK8 的 JEP 101: Generalized Target-Type Inference

我看楼上有回答说 Java8 以下版本不可以编译你的代码,这是不对的。使用 Java7 也可以正确编译你的代码,因为 JDK7 对于泛型类型的推导不仅仅只有 “菱形推导”,还添加了部分目标类型推导的功能,可以参考 Oracle 的官方文档 Type Inference,看到 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:

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.

可以看到,对于 JDK7 以及之后版本的 JDK 来说,它们至少都已经拥有了根据返回值来确定泛型类型的能力。


再回到你的代码,比如你写:

List<Snow> snow1 = Arrays.asList(

new Crusty(), new Slush(), new Powder()

);

编译器可以根据你的返回值类型(List<Snow>)来推断出你传入的每一个参数都是一个 Snow,从而判断参数是否正确(即判断每一个参数的类型是否都是 Snow 或者其子类)。对于下面的代码效果一样:

List<Snow> powders = Arrays.asList(

new Light(), new Heavy()

);

因为 LightHeavy 都是 Snow 的间接子类。


如果你不写上返回值,而是直接写上代码:

Arrays.asList(

new Light(), new Heavy()

);

然后用 IntelliJ IDEA 的自动生成返回的变量的功能(我认为 IDE 也是调用的编译器提供的相关 API 的功能,或者至少是符合编译器要求的功能):
【java】java中 Arrays.asList返回的泛型类型如何确定?

你会发现生成的是 List<Powder>
【java】java中 Arrays.asList返回的泛型类型如何确定?

这种时候,没有返回值,那么编译器会尝试去找到传入的所有参数的最近的交集,即 Powder,所以编译推导出此时的 T 的类型为 Powder


综上,Java 的编译器,从很早以前就真的开始变得越来越聪明了呢。

书比较老,Java8以下版本都不能编译,Java8可以,你估计用的是Java8或更高版本。

表示我用java8没通过

【java】java中 Arrays.asList返回的泛型类型如何确定?

以上是 【java】java中 Arrays.asList返回的泛型类型如何确定? 的全部内容, 来源链接: utcz.com/a/74016.html

回到顶部