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

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

什么是 SAM 类型? 什么时候可以使用 SAM 的示例场景?

43473 次浏览

总结一下 Jon 发布的链接1,“ SAM”代表“单个抽象方法”,而“ SAM-type”指的是 RunnableCallable等接口。Lambda 表达式是 Java8中的一个新特性,它被认为是 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.compare不同名的特定方法创建一个 Comparator,这样您就不必遵循方法的接口命名,您可以在一个类中重写多个比较,然后通过 lambda 表达式动态创建比较器。

继续深入。

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

这种方法很复杂,甚至包括可以直接从堆栈内存读取原始值和引用并传递到 Lambda 代码的代码(例如,为了避免需要分配 Object[]数组来调用 Lambda) ,但它允许 Lambda 实现的未来迭代替换旧的实现,而不必担心字节码兼容性。如果 Oracle 的工程师在更新版本的 JVM 中更改了底层的 Lambda 实现,那么在旧版本的 JVM 上编译的 Lambda 将自动使用更新的实现,而不需要开发人员进行任何更改。


链接上的语法已经过时了。查看 表达式以查看当前语法。