什么是Java中的"SAM类型"?

Cod*_*ody 116 java lambda java-8

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

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

Bri*_*ian 128

总结乔恩发布的链接1的情况下,它不断下降,"SAM"代表"单一抽象方法"和"SAM型"是指接口等Runnable,Callable等Lambda表达式,在Java 8中的新功能,是被认为是SAM类型,可以自由转换为它们.

例如,使用如下界面:

public interface Callable<T> {
    public T call();
}
Run Code Online (Sandbox Code Playgroud)

你可以声明一个Callable像这样的lambda表达式:

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

在这种情况下,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);
Run Code Online (Sandbox Code Playgroud)

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

走得更远......

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

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


1链接上的语法已过期.查看Lambda Expressions Java Trail以查看当前语法.