以下类将不会编译:

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节)时,实际上会忽略它,以便 Select 正确的println调用.

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

在解析目标类型之前,隐式类型的lambda表达式或不精确的方法引用表达式的含义足够模糊,因此包含这些表达式的参数被认为与适用性无关;在重载解析完成之前,只会忽略它们(除了它们预期的arity).

因此,除了将其传递给System.out.println之外,bar的返回类型实际上没有任何界限——它可以是任何引用类型.编译器try 使用上面第二个链接中的规则推断我们要调用的重载,但println(String)println(char[])似乎都是有效的重载!

其他线路工作的原因如下:

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

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

    这些lambda都与适用性相关,因为它们是explicitly typed lambdas(尽管此规则有一些例外).请注意,没有参数的lambda被视为显式类型的lambda.

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

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

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

    只有一个重载println可供 Select ,因此即使我们不知道bar从lambda返回String,我们仍然可以通过看到bar调用被传递到println(String)来实现.

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

Java相关问答推荐

在FML中删除关键帧动画

找到允许的最大底片

将成为一个比较者.比较…在现代Java中,编译器会对`CompareTo`方法进行优化吗?

填写文本字段后锁定PDF

Android Studio—java—在onBindViewHolder中,将断点和空白添加到BackclerView中

ApachePOI:不带换行的新行

JavaFX Maven Assembly插件一直打包到错误的JDK版本

使用Testcontainers与OpenLiberty Server进行集成测试会抛出SocketException

在bash中将数组作为Java程序的参数传递

在springboot 3中,当我调用api endpoint时,会出现404

使用Spring Boot3.2和虚拟线程的并行服务调用

什么是Java原子属性的正确getter和setter

GSON期间的Java类型擦除

用户填充的数组列表永不结束循环

如何从日志(log)行中删除包名称?

如何获得凌空cookies ,并设置它在下一个请求- android

在应用程序运行时更改LookAndFeel

Android无法在Java代码中调用Kotlin代码,原因是在Companion中使用Kotlin枚举时

如何将RESTAssured';S的Http标题转换为<;字符串、字符串和>的映射?

Spring Mapstruct如何获取Lazy初始化实体字段的信息?