如果是这样,那么这是否意味着 lambdas 并不是真正独立的方法,而是一种被引入语言的新型元素,
正确,lambda 被编译成具有合成名称的普通方法
但只是表达匿名类的一种更紧凑的方式,因此只是在编译器方面添加了工具(就像泛型一样)?
不,它不仅在编译器方面。 JVM里面也有代码,这样编译器就不用为lambda写class文件了。
此外,方法引用如何遵守这一点,特别是不与任何对象关联的静态方法?
方法引用与 lambda 没有什么不同:在运行时必须有一个对象实现功能接口。在调用对象的“SAM”时,此方法将调用引用的方法。
例如,当对实例方法的方法引用分配给功能接口时,则使用该方法的封装对象,
不,不能使用。让我们使用System.out::println 方法引用来举个例子:
Arrays.asList("A", "B").forEach(System.out::println);
List<E>.forEach() 需要一个 Consumer<? super E>,它定义了方法 void accept(E e)。编译器需要在类文件中生成字节码和其他信息,以便在运行时 JVM 可以生成一个使用方法void accept(E e) 实现Consumer<E> 的类。这个生成的方法然后调用System.out.println(Object o)。
运行时生成的类看起来像
class $$lambda$xy implements Consumer<Object> {
private PrintStream out;
$$lambda$xy(PrintStream out) {
this.out = out;
}
void accept(Object o) {
out.println(o);
}
}
评论中的问题:“为什么不直接分配给实例及其方法?”
让我们稍微扩展一下这个例子:
static void helloWorld(Consumer<String> consumer) {
consumer.apply("Hello World!");
}
public static void main(String[] args) {
helloWorld(System.out::println);
}
要编译它,编译器必须生成字节码,该字节码创建一个实现Consumer<String> 的对象(因此它可以将对象传递给helloWorld())。该对象必须以某种方式存储在调用accept(x) 方法时必须在System.out PrintStream 上调用println(x) 的信息。
对于此类对象,其他语言可能有其他名称或概念 - 在 Java 中,已确立的概念是“实现接口的匿名类和该匿名类的对象”。
对象如何存储这些信息?好吧,你可以发明一些超级酷的新方法来存储这些信息。 Java 语言设计者认为匿名类就足够了——暂时。但他们有先见之明,如果有人提出一个新想法以更有效的方式实现它,这应该很容易集成到 Java 生态系统(Java 编译器和 JVM)中。
所以他们还决定不在编译时创建匿名类,而是让编译器将必要的信息写入类文件。现在 JVM 可以在运行时决定存储信息的最佳方式(在正确的对象上调用正确的方法)。