java - 奇怪的编译问题

标签 java compiler-errors

以下类将无法编译:

public class Thing {

    public static <T> T foo(java.util.function.Supplier<T> supplier) {
        return supplier.get();
    }

    public static <T> T bar(java.util.function.Function<Integer, T> function) {
        return function.apply(42);
    }

    public static void main(String... args) {
        System.out.println(foo(() -> "hello")); // 1
        System.out.println(bar(any -> "hello")); // 2 !!!
        System.out.println(bar((Integer any) -> "hello")); // 3
        System.out.println(Thing.<String>bar(any -> "hello")); // 4
        println(bar(any -> "hello")); // 5
    }

    private static void println(String string) {
        System.out.println(string);
    }
}

问题出在 main() 方法中的第 [2] 行(所有其他行都很好):

[ERROR] .../Thing.java:[13,19] reference to println is ambiguous
    both method println(char[]) in java.io.PrintStream and method println(java.lang.String) in java.io.PrintStream match
[ERROR] .../Thing.java:[13,31] incompatible types: inference variable T has incompatible bounds
    lower bounds: char[],java.lang.Object
    lower bounds: java.lang.String

我不明白为什么编译器认为 bar() 的返回类型存在歧义(char[]String) 并且无法决定应使用哪种类型的 println() 方法。在第[1]行,可以推断出foo()的返回类型是String,在第[3]行,我不明白如何指定any (Integer) 会有所帮助,因为给定 bar() 方法的签名,它不可能是其他任何东西。

最佳答案

这是因为隐式类型的 lambda any -> "hello"不是pertinent to applicability ,因此在确定调用 bar(any -> "hello") 的类型时实际上会忽略它。应该是(参见 JLS 的 Invocation Type Inference 部分),以便选择正确的 println打电话。

理由可以从第一个链接找到:

The meaning of an implicitly typed lambda expression or an inexact method reference expression is sufficiently vague prior to resolving a target type that arguments containing these expressions are not considered pertinent to applicability; they are simply ignored (except for their expected arity) until overload resolution is finished.

除了我们将其传递给 System.out.println 之外, bar 的返回类型实际上没有限制无论如何 - 它可以是任何引用类型。编译器尝试使用上面第二个链接中的规则来推断我们尝试调用哪个重载,但是 println(String)println(char[])两者看起来都是有效的重载!

其他线路工作的原因是:

  • System.out.println(foo(() -> "hello"));

  • System.out.println(bar((Integer any) -> "hello"));

    这些 lambda 都与适用性相关,因为它们是 explicitly typed lambdas (尽管此规则有一些异常(exception))。请注意,不带参数的 lambda 算作显式类型化 lambda。

  • System.out.println(Thing.<String>bar(any -> "hello"));

    由于您提供了 bar 的类型参数,编译器不需要使用类型推断来确定它。现在可以简单地确定bar返回String ,所以您必须调用println(String) .

  • println(bar(any -> "hello"));

    println 只有一个重载来选择,所以即使我们不知道 bar返回String从 lambda 来看,我们仍然可以通过看到 bar 来做到这一点。调用正在转接至 println(String) .

我建议您查看与适用性无关的表达式列表并尝试使用它们:)

关于java - 奇怪的编译问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72742169/

相关文章:

java - 如何使变量在后台每秒递增

javascript - 尝试在 Nashorn 上运行 browser.js 时 <eval> 中的 char 类中的空范围

java - Windows 和 Unix 之间的 ZoneDateTime 精度差异

java - Java ArrayList自变量给出预期的<Identifier>

c++ - 使用CMake链接Boost(Ubuntu 14.04)

java - jdk 8 不允许 switch 语句中出现字符串?为什么

java - ListView在android中打开超链接

android - Android Studio 3.0 Beta5编译错误

c++ - 无法构建 lldb - 'atomic' 文件未找到

c# - 为什么C#限制了可以声明为const的类型集?