我有以下课程:
import java.util.HashSet;
import java.util.List;
public class OverloadTest<T> extends HashSet<List<T>> {
private static final long serialVersionUID = 1L;
public OverloadTest(OverloadTest<? extends T> other) {}
public OverloadTest(HashSet<? extends T> source) {}
private OverloadTest<Object> source;
public void notAmbigious() {
OverloadTest<Object> o1 = new OverloadTest<Object>(source);
}
public void ambigious() {
OverloadTest<Object> o2 = new OverloadTest<>(source);
}
}
这在JDK 7的javac以及eclipse(合规性设置为1.7或1.8)下编译得很好。但是,尝试在JDK 8的javac下编译,我收到以下错误:
[ERROR] src/main/java/OverloadTest.java:[18,35] reference to OverloadTest is ambiguous
[ERROR] both constructor <T>OverloadTest(OverloadTest<? extends T>) in OverloadTest and constructor <T>OverloadTest(java.util.HashSet<? extends T>) in OverloadTest match
请注意,此错误仅适用于ambigous()
方法中的构造函数调用,而不适用于notAmbiguous()
方法中的构造函数调用。唯一的区别是ambiguous()
依赖于钻石运算符。
我的问题是:JDK 8下的javac是否正确标记了一个模棱两可的解决方案,还是JDK 7下的javac未能解决模糊问题?根据答案,我需要提交JDK错误或ecj错误。
答案 0 :(得分:3)
在调用中,当使用T set显式调用构造函数时,没有歧义:
OverloadTest<Object> o1 = new OverloadTest<Object>(source);
因为T是在构造函数调用时定义的,所以Object传递了?在编译时扩展Object检查就好了,没有问题。当T明确设置为Object时,两个构造函数的选择变为:
public OverloadTest(OverloadTest<Object> other) {}
public OverloadTest(HashSet<Object> source) {}
在这种情况下,编译器很容易选择第一个。在另一个示例中(使用菱形运算符)未明确设置T,因此编译器首先尝试通过检查实际参数的类型来确定T,这是第一个选项不需要做的。
如果第二个构造函数被更改为正确反映我想象的是期望的操作(因为OverloadTest是T的列表的HashSet,那么传入T的列表的HashSet应该是可能的),如下所示:
public OverloadTest(HashSet<List<? extends T>> source) {}
......然后解决了歧义。但是,当你要求编译器解决这种模糊的调用时,目前就会出现冲突。
编译器将看到菱形运算符,并将尝试根据传入的内容以及各种构造函数所期望的内容来解析T.但是编写HashSet构造函数的方式将确保无论传入哪个类,两个构造函数都将保持有效,因为在擦除之后,T总是被Object替换。当T是Object时,HashSet构造函数和OverloadTest构造函数具有相似的擦除,因为OverloadTest是HashSet的有效实例。并且因为一个构造函数不会覆盖另一个(因为OverloadTest&lt; T&gt;不会扩展HashSet&lt; T&gt;),实际上不能说一个比另一个更具体,所以它赢了&# 39;知道如何做出选择,而是抛出编译错误。
这只会发生,因为通过使用T作为边界,您强制执行编译器进行类型检查。如果你只是做了它&lt;?&gt;而不是&lt;?延伸T>它会编译得很好。 Java 8编译器对Java 7的类型和擦除更为严格,部分原因是Java 8中的许多新功能(如界面防御方法)要求他们对泛型更加迂腐。 Java 7没有正确报告这些内容。
答案 1 :(得分:0)
很抱歉破坏了聚会,但Java中的重载解析比目前看到的评论和答案更复杂。
两个构造函数都适用:
OverloadTest(OverloadTest<? extends T>)
OverloadTest(HashSet<? extends T>)
此时Java 8使用"More Specific Method Inference",它分析所有候选对。
在检查前一个构造函数是否比后者更具体时,会成功解析以下约束( T#0 是{{1}的类型参数T
的推理变量}}):
HashSet
解决方案是将 T#0 实例化为OverloadTest<? extends T> <: HashSet<? extends T#0>
。
反向尝试无法解决以下约束(此处 T#0 是List<T>
的类型参数T
的推理变量):
OverloadTest
无法找到 T#0 的实例化来满足该约束条件。
由于一个方向成功而另一个方向失败,前一个构造函数被认为比后者更具体,从而解决了歧义。
Ergo:接受该程序似乎是编译器的正确答案。
PS:我并不反对Java 8 中必要的更多迂腐类型检查,但事实并非如此。