有没有人理解为什么以下代码在Java 7及更低版本中可以正常编译,但在Java 8中失败。
public static void main(String[] args) throws Exception {
put(get("hello"));
}
public static <R> R get(String d) {
return (R)d;
}
public static void put(Object o) {
System.err.println("Object " + o);
}
public static void put(CharSequence c) {
System.err.println("CharSequence " + c);
}
public static void put(char[] c) {
System.err.println("char[] " + c);
}
get方法具有泛型返回类型。在JDK 7及更低版本中,这将编译精细,并选择带有Object参数的put方法。在JDK 8中,这不能编译,表明put方法不明确。
显然JDK 8正在跳过Object-parameter方法并找到最后两个子Object-parameter方法并抱怨它们(即如果你添加另一个put方法和其他一些参数类型,编译器会切换并抱怨新的最后两种方法)
这似乎是一个错误。
答案 0 :(得分:30)
您的问题是广义目标类型推断的副作用,这是Java 8的改进。
让我们采用您的示例方法,
public static <R> R get(String d) {
return (R)d;
}
现在,在上面的方法中,编译器无法解析通用参数R
,因为R
没有参数。
因此,他们引入了一个名为Target-type Inference
的概念,该概念允许根据赋值参数推断参数。
所以,如果你这样做,
String str = get("something"); // R is inferred as String here
Number num = get("something"); // R is inferred as Number here
这在Java 7中运行良好。但以下不,
put(get("something");
static void Put(String str) {} //put method
因为类型推断仅适用于直接分配。
如果没有直接分配,则通用类型推断为Object
。
因此,当您使用Java 7编译代码时,您的put(Object)
方法被调用没有任何问题。
他们改进了类型推断以从方法调用和链式方法调用
推断出类型现在,您可以直接调用 但是如您所知,方法 告诉编译器你想要什么,put(get("something"))
,并根据put()
方法的参数推断。 / p>
put(Charsequence)
和put(char[])
匹配参数。所以存在歧义。修正?
put(TestClass.<CharSequence>get("hello")); // This will call the put(CharSequence) method.
答案 1 :(得分:2)
看起来这是一种已知的不兼容性。
请参阅this article.的“区域:工具/ javac”部分 并this bug。
概要
以下在JDK 7中编译并带有警告的代码将无法在JDK 8中编译:
import java.util.List;
class SampleClass {
static class Baz<T> {
public static List<Baz<Object>> sampleMethod(Baz<Object> param) {
return null;
}
}
private static void bar(Baz arg) {
Baz element = Baz.sampleMethod(arg).get(0);
}
}
在JDK 8中编译此代码会产生以下错误:
SampleClass.java:12: error:incompatible types: Object cannot be converted to Baz
Baz element = Baz.sampleMethod(arg).get(0);
Note: SampleClass.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
在此示例中,将原始类型传递给 sampleMethod(Baz)方法适用于子类型(参见 JLS,Java SE 7 Edition,第15.12.2.2节)。
未经检查 转换对于适用的方法是必要的,因此它的返回 类型被删除(参见JLS,Java SE 7 Edition,第15.12.2.6节)。在 这种情况下sampleMethod(Baz)的返回类型是 java.util.List而不是java.util.List&gt;因此 返回类型get(int)是Object,它不是赋值兼容的 与Baz。