Java 8:对[方法]的引用含糊不清

时间:2015-08-30 07:07:31

标签: java generics compiler-errors java-8

有没有人理解为什么以下代码在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方法和其他一些参数类型,编译器会切换并抱怨新的最后两种方法)

这似乎是一个错误。

2 个答案:

答案 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)方法被调用没有任何问题。

他们在Java 8中做了什么

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

推断出类型

有关他们的更多详情herehere

现在,您可以直接调用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。