Java 8:对的引用不明确

有没有人理解为什么以下代码可以在Java 7及更低版本中正常编译,但是在Java 8中失败了。

public static void main(String[] args) throws Exception { put(get("hello")); } public static  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方法具有generics返回类型。 在JDK 7及更低版本中,这将编译精细,并选择带有Object参数的put方法。 在JDK 8中,这无法编译,表明put方法不明确。

显然JDK 8正在跳过Object-parameter方法并找到最后两个子Object-parameter方法并抱怨它们(即如果你添加另一个put方法和其他一些参数类型,编译器将切换并抱怨新的last两种方法)

这似乎是一个错误。

您的问题是广义目标类型推断的副作用,这是Java 8的改进。

什么是目标类型推断

我们来看你的例子方法,

 public static  R get(String d) { return (R)d; } 

现在,在上面的方法中,编译器无法解析generics参数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 

因为类型推断仅适用于直接分配。

如果没有直接赋值,那么generics类型被推断为Object

因此,当您使用Java 7编译代码时,调用了put(Object)方法而没有任何问题。

他们在Java 8中做了什么

他们改进了类型推断 ,从方法调用链式方法调用推断出类型

关于他们的更多细节在这里和这里

所以现在,你可以直接调用put(get("something"))并且将根据put()方法参数 推断generics类型。

但是如你所知,方法put(Charsequence)put(char[])匹配参数。 所以有歧义。

固定?

只需告诉编译器你想要什么,

 put(TestClass.get("hello")); // This will call the put(CharSequence) method. 

看起来这是一个已知的不兼容性。

请参阅本文的“区域:工具/ javac”部分。 而这个错误 。

概要

在JDK 7中编译带有警告的以下代码将无法在JDK 8中编译:

 import java.util.List; class SampleClass { static class Baz { public static List> sampleMethod(Baz 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>,因此get(int)的返回类型是Object,它与Baz不是赋值兼容的。