【问题标题】:Java 8: Reference to [method] is ambiguous [duplicate]Java 8:对 [方法] 的引用不明确 [重复]
【发布时间】:2015-11-24 11:28:42
【问题描述】:

有谁知道为什么下面的代码在 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 方法,编译器将切换并抱怨新的最后两种方法)

这似乎是一个错误。

【问题讨论】:

  • 你确定吗,用Java7编译得很好,而不是Java8
  • get 方法是假的。你期望R 是什么?从分配给的值推断?直接调用put有2种选择,那么应该使用哪个R呢?将未经验证的演员隐藏在泛型后面确实糟糕
  • 这段代码在我的 IDE 上使用 Java 8 编译器编译没有错误。在返回通用 get 方法时,只有关于类型安全的警告
  • 为什么一般投一个StringStringfinal 并直接扩展 Object 所以您唯一可以将其转换为 Object
  • @Parker,但是String 实现了3个接口,所以你也可以将它转换为任何一个接口...

标签: java generics compiler-errors java-8


【解决方案1】:

您的问题是 Generalized Target-type Inference(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()方法的参数推断

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

修复?

直接告诉编译器你想要什么,

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

【讨论】:

  • “链式方法调用” - 你的意思是嵌套方法调用吗?通常“链式”的意思是“foo().bar()”
  • 有 3 种方式来提供目标类型 - 赋值;方法参数;铸件。所以我们也可以使用强制转换上下文来解决它 - put( (CharSequence)get("hello") );
  • 是的,会的,但它不包含在 java8 中 :)
  • 正确的解决方法是更改​​那个无意义的get 方法,而不是调整它的类型推断。只要那个方法也允许put(TestClass.&lt;char[]&gt;get("hello"));,它就坏了。
  • @Ken,检查this SO,这解释了为什么没有选择对象。简而言之,推断出最具体的类型。
【解决方案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 版,第 15.12.2.2 节)。

未经检查的 转换对于该方法适用是必要的,因此它的返回 类型被擦除(参见 JLS,Java SE 7 版,第 15.12.2.6 节)。在 这种情况下 sampleMethod(Baz) 的返回类型是 java.util.List 而不是 java.util.List> ,因此 get(int) 的返回类型是 Object,不兼容赋值 和巴兹一起。

【讨论】:

  • 嗯?这似乎不是“不兼容”;这是javac7中的一个bug,在javac8中已修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-07
相关资源
最近更新 更多