类型推断算法的哪些变化会导致这种行为?

时间:2016-01-19 11:19:02

标签: java generics type-inference

我正在阅读OCP Java SE7, certification guide from Mala Gupta。在页297,以下代码段

import java.util.HashMap;
import java.util.Map;

public class TestGenericTypeInference {
    Map<String,Double> salaryMap     = new HashMap<>();
    Map<String,Object> copySalaryMap = new HashMap<>(salaryMap);
}

正在使用java 8进行编译,但是使用java 7编译器会抱怨:

TestGenericTypeInference.java:8: error: incompatible types: HashMap<String,Double> cannot be converted to Map<String,Object>
    Map<String,Object> copySalaryMap = new HashMap<>(salaryMap);  
                                   ^

我的问题是:类型推断算法的哪些变化会导致这种行为?

3 个答案:

答案 0 :(得分:1)

我认为它在JLS 8, ch 18.2.1中描述:

  

通过将嵌套的泛型方法调用视为多重表达式,我们   改进嵌套调用的推理行为。例如,   以下内容在Java SE 7中是非法的,但在Java SE 8中是合法的:

     

ProcessBuilder b = new ProcessBuilder(Collections.emptyList()); // ProcessBuilder's constructor expects a List<String>

     

当外部和嵌套调用都需要推理时,   问题更难。例如:

     

List<String> ls = new ArrayList<>(Collections.emptyList());

     

我们的   方法是“提升”为嵌套调用推断的边界   (在{ α <: Object }的情况下只需emptyList)到外部   推理过程(在这种情况下,试图推断β在哪里   构造函数用于类型ArrayList<β>)。我们还推断出依赖关系   嵌套推理变量和外推理之间   变量(约束‹List<α> → Collection<β>>将减少到   依赖α = β)。通过这种方式,解析推断   嵌套调用中的变量可以等到额外的   信息可以从外部调用推断出来(基于   分配目标,β = String)。

此示例List<String> ls = new ArrayList<>(Collections.emptyList());也未在java-7中编译。

答案 1 :(得分:1)

查看以下链接:
https://forums.manning.com/posts/list/36712.page
https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

  

Java编译器利用目标类型来推断泛型方法调用的类型参数。

static <T> List<T> emptyList();
List<String> listOne = Collections.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类型的值,该值与方法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());

答案 2 :(得分:1)

我的问题的答案:

  

类型推断算法的哪些变化会导致此行为?

the Generics FAQ from Angelina Langer。给出了一个类似的例子:

// error in Java 7 ; fine since Java 8 
Set<Number> s3 = new HashSet<>(Arrays.asList(0L,0L));   
  
      
  • [...]表达式表明确实忽略了赋值的左侧(Java 7中的 )。编译器再次从构造函数参数(即asList方法的结果)推断出新HashSet的缺失类型参数必须为Long。这导致类型不匹配和相应的错误消息。编译器不会断定缺少的类型参数应该是Number,因为它忽略了赋值的左侧。 在Java 8 中,类型推断得到了修改和改进。从那时起,编译器将Number作为类型参数从编译器右侧的新HashSet中推断出来,并从中推导出Number作为asList方法的类型参数。在Java 8中,这编译得很好。
  •