Java 中的“SAM 类型”是什么?

问题描述 投票:0回答:1

在阅读 Java-8 规范时,我不断看到对“SAM 类型”的引用。我无法找到这是什么的明确解释。

什么是 SAM 类型以及可能使用该类型的示例场景是什么?

java lambda java-8 terminology
1个回答
187
投票

总结一下 Jon 发布的链接1,以防万一它失效了,“SAM”代表“单一抽象方法”,“SAM 类型”指的是

Runnable
Callable
等接口。 Lambda 表达式是 Java 8 中的一项新功能,被视为 SAM 类型,可以自由转换为它们。

例如,使用这样的界面:

public interface Callable<T> {
    public T call();
}

您可以使用 lambda 表达式来声明

Callable
,如下所示:

Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"

在这种情况下,Lambda 表达式大多只是语法糖。它们在代码中看起来比匿名类更好,并且对方法命名的限制较少。从链接中获取这个例子:

class Person { 
    private final String name;
    private final int age;

    public static int compareByAge(Person a, Person b) { ... }

    public static int compareByName(Person a, Person b) { ... }
}

Person[] people = ...
Arrays.sort(people, Person::compareByAge);

这会使用与

Comparator
不共享相同名称的特定方法创建
Comparator.compare
,这样您就不必遵循方法的接口命名,并且可以在类中进行多个比较覆盖,然后通过 lambda 表达式动态创建比较器。

深入...

在更深层次上,Java 使用 Java 7 中添加的

invokedynamic
字节码指令来实现这些。我之前说过,声明 Lambda 会创建
Callable
Comparable
的实例,类似于匿名类,但这并不是严格意义上的 是的。相反,第一次调用
invokedynamic
时,它会使用
LambdaMetafactory.metafactory
方法
2 创建一个 Lambda 函数处理程序,然后在以后的 Lambda 调用中使用此缓存实例。更多信息可以在这个答案中找到。

这种方法很复杂,甚至包括可以直接从堆栈内存读取原始值和引用以传递到 Lambda 代码中的代码(例如,避免需要分配

Object[]
数组来调用 Lambda),但它允许未来迭代的 Lambda 实现来替换旧的实现,而不必担心字节码兼容性。如果 Oracle 的工程师更改了较新版本 JVM 中的底层 Lambda 实现,则在旧版 JVM 上编译的 Lambda 将自动使用较新的实现,而无需开发人员进行任何更改。


1 链接上的语法已过时。查看 Lambda 表达式 Java Trail 以了解当前语法。

2 11 年过去了,

LambdaMetafactory.metafactory
仍然是 JDK 23 中的实现方式

© www.soinside.com 2019 - 2024. All rights reserved.