2 回答

TA貢獻2011條經驗 獲得超2個贊
這SayHello
是一個單一抽象方法接口,它有一個接受字符串并返回 void 的方法。這類似于消費者。您只是以使用者的形式提供該方法的實現,這類似于以下匿名內部類實現。
SayHello?sh?=?new?SayHello()?{ ????@Override ????public?void?speak(String?message)?{ ????????System.out.println("Hello?"?+?message); ????} }; names.forEach(n?->?sh.speak(n));
幸運的是,您的接口有一個方法,以便類型系統(更正式地說,類型解析算法)可以將其類型推斷為SayHello
.?但如果有 2 個或更多方法,這是不可能的。
然而,更好的方法是在 for 循環之前聲明使用者并使用它,如下所示。聲明每次迭代的實現都會創建不必要的對象,這對我來說似乎違反直覺。這是使用方法引用而不是 lambda 的增強版本。這里使用的有界方法引用調用上面聲明的實例上的相關方法hello
。
SayHello?hello?=?message?->?System.out.println("Hello?"?+?message); names.forEach(hello::speak);
更新
考慮到對于無狀態 lambda,僅在創建實例時才不會從其詞法范圍捕獲任何內容,這兩種方法都僅創建 的一個實例SayHello
,并且遵循建議的方法沒有任何收益。然而,這似乎是一個實現細節,直到現在我才知道。因此,更好的方法是將消費者傳遞給您的 forEach,如下面評論中所建議的。另請注意,所有這些方法都僅創建SayHello
界面的一個實例,而最后一種更簡潔。它看起來是這樣的。
names.forEach(message?->?System.out.println("Hello?"?+?message));
以下是 JLS?§15.27.4的相關部分:Lambda 表達式的運行時求值
這些規則旨在為 Java 編程語言的實現提供靈活性,因為:
不需要在每次評估時分配新對象。
事實上,我最初認為每次評估都會創建一個新實例,這是錯誤的。

TA貢獻1853條經驗 獲得超6個贊
foreach 的簽名如下所示:
void forEach(Consumer<? super T> action)
它接受 Consumer 對象。Consumer 接口是一個函數式接口(具有單個抽象方法的接口)。它接受輸入并且不返回結果。
定義如下:
@FunctionalInterface
public interface Consumer {
void accept(T t);
}
因此,任何實現,您的情況都可以用兩種方式編寫:
Consumer<String> printConsumer = new Consumer<String>() {
public void accept(String name) {
SayHello hello = (message) -> System.out.println("Hello " + message);
hello.speak(n);
};
};
或者
(n) -> {
SayHello hello = (message) -> System.out.println("Hello " + message);
hello.speak(n);
}
類似地,SayHello 類的代碼可以寫為
SayHello sh = new SayHello() {
@Override
public void speak(String message) {
System.out.println("Hello " + message);
}
};
或者
SayHello hello = message -> System.out.println("Hello " + message);
因此,names.foreach在內部首先調用consumer.accept方法并運行其lambda/匿名實現,該實現創建并調用SayHello lambda實現等等。使用lambda表達式,代碼非常緊湊、清晰。您唯一需要了解它是如何工作的,即在您的情況下它使用消費者界面。
所以最終流程:foreach -> consumer.accept -> sayhello.speak
添加回答
舉報