我正在阅读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);
^
我的问题是:类型推断算法的哪些变化会导致这种行为?
答案 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中,这编译得很好。