如果是這樣,那麼是否意味着lambda表達式是不是真的獨立的方法。作爲這樣的新類型的元素帶入語言,
正確,lambda表達式被編譯成一個合成常規方法名稱
但是隻是表達一個匿名類的更簡潔的方式,因此僅僅是在編譯器端添加設施(就像泛型一樣)?
不,它不僅在編譯器方面。在涉及的JVM中也有代碼,因此編譯器不必爲lambda表編寫類文件。
此外,方法引用如何符合那個,特別是與任何對象沒有關聯的靜態方法?
方法引用與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)
方法有調用println(x)
在System.out
PrintStream的信息。
其他語言可能有其他名稱或概念的這種對象 - 在Java中建立的概念是「匿名類實現接口和匿名類的對象」。
如何對象存儲這些信息?那麼,你可以發明一些超酷的新方式來存儲這些信息。 Java語言設計師決定,一個匿名類將會足夠好 - 暫時。但他們有先見之明,如果有人提出一個更有效的新思路來實現它,這應該很容易集成到Java生態系統(Java編譯器和JVM)中。
所以,他們也決定創建匿名類在編譯時間,但讓編譯器只寫了必要的信息在類文件。現在JVM可以在運行時確定存儲信息的最佳方式(在正確的對象上調用正確的方法)是什麼。
的JLS上lambda表達式[第15.27](https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.27)和[段15.13(HTTPS ://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.13)方法引用爲您提供了答案。 –