gpt4 book ai didi

Java - 为什么具有匹配签名的 SAM 类型不可互换?

转载 作者:行者123 更新时间:2023-12-05 02:26:15 25 4
gpt4 key购买 nike

Java 可以正确推断 SAM 类型并允许我传递 lambda 作为它们实现的替代,但是当我尝试将一种 SAM 类型转换为具有相同签名的另一种时,相同的机制失败了:

    public static Supplier<String> saySomething(){
return () -> "Hello";
}

@FunctionalInterface
interface Greeter {
String greet();
}

public static Greeter getGreeting1(){
// this compiles and runs just fine
return () -> saySomething().get();
}

public static Greeter getGreeting2(){
// this fails at compile:
// java: incompatible types: java.util.function.Supplier<java.lang.String> cannot be converted to Greeter
return saySomething();
}

public static Greeter getGreeting3(){
// this compiles without warnings(probably only because a Supplier<String> might
// also implement the Greeter interface, by chance) but if you call it will fail
// at runtime with a java.lang.ClassCastException
return (Greeter) saySomething();
}

我试图理解为什么 JVM 不会让 lambda 被当作这里的一件事和那里的另一件事,因为它只需要为此进行另一个 SAM 转换。我怀疑这是因为 lambda 被提升为隐藏的匿名类,但有人可以给出明确的答案吗?
此外,是否有任何解决方法(除了将每个 SAM 转换包装在另一个 lambda 中,如第一个示例中所示)以允许它工作?

最佳答案

Lambda 表达式是一种 Java 语言工件,而不是 JVM 功能。

其中涉及多个编译时功能,这些功能在运行时不存在,而且在运行时执行起来成本太高。

  • 编译器使用类型推断,结合通用类型系统,来确定要实现的接口(interface)。

  • 然后检查接口(interface)的潜在复杂类型层次结构,包括哪个方法重写哪个,再次考虑泛型类型系统的规则,最终确定哪些抽象方法(不包括 privatedefaultstatic 方法)不匹配 java.lang.Objectpublic 方法是留待实现。

  • 如果只有一个方法可以实现,erasure方法和所有Bridge Methods被识别,以包含在构建第一步中推断的功能接口(interface)实例的配方中。

整体操作是一个object creation ,尽管实现可能会重用现有对象:

At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object.

相比之下,类型转换是 type checking表达式:

A cast expression […] checks, at run time, that a reference value refers to an object either whose class is compatible with a specified reference type or list of reference types […]

如果成功,结果仍然是引用同一个对象。所以如果你有

interface A {
int test();
}
interface B extends A {
default int test() {
return method2() + 1;
}
int method2();
}
B b1 = () -> 10;
A a1 = (A)b1;
System.out.println(a1.test());

它将打印 11,因为变量 a1 仍然包含对与 b1 相同的 B 实例的引用> 及其 test() 方法实现为 method2() + 1

相比之下,

A a2 = () -> 10;
System.out.println(a2.test());

将打印 10 ,因此,很明显这不能是与上面的 b1 相同的对象,无论对象的创建方式如何(即相同lambda 表达式 () -> 10).

将此 A 实例转换为 B,如 B b2 = (B)a2; 假设其 method2() 由于兼容的功能签名不可能像 test() 一样实现,因为它会导致关于结果的 test() 方法的矛盾行为(如果仍然不相信,想想如果我们再次将 b2 转换回 A 会发生什么。

相反,当你使用像这样的方法引用时

B b2 = a2::test;

您明确表示 b2 将是与 a2b2 的功能方法(method2())不同的对象) 将表现出与 a2 的功能方法 (test()) 相同的行为。因此,b2test() 方法与 a2test() 方法具有不同的行为是一致的> 方法。

关于Java - 为什么具有匹配签名的 SAM 类型不可互换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73981904/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com