[問題] method reference

作者: jtorngl (Pedrosa go!)   2019-08-21 14:34:30
最近才開始看 lambda expression
直接看程式碼都覺得很難看懂
有種必須以 compiler 的角度來看才知道
特別是 lambda 只要符合 function descriptor 都可編譯
但是 target type 會是什麼,第一時間還真不知道是什麼
然後在練習 method reference 時,有一個地方一直無法理解
請看下面程式碼部份的中文
另問,如果要問問題,有什麼比較好貼出程式碼的地方嗎?
js 常看到 js fiddle
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class MethodReference {
public static void main(String[] args) {
List<String> strList = Arrays.asList("a", "b", "x");
/* == anonymous class == */
System.out.println("== anonymous class ==");
strList.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.print(s + ",");
}
}); // a,b,x,
/*
* == lambda expression ==
* Consumer<String> abstract method: void accept(String s);
* function descriptor: str -> void
*/
System.out.println("\n== lambda expression ==");
strList.forEach( str -> System.out.print(str + ",") );
strList.forEach( str -> {} );
strList.forEach( str -> {return;} );
/* Error: incompatible types: bad return type in lambda expression */
// strList.forEach( str -> "[" + str + "]" );
這裡使用 lambda 時,回傳值非 void 時會編譯錯誤
我是用,因為 forEach() 要的是 Consumer
所以要符合 Consumer 的 function descriptor 去理解
/* == Class::staticMethod == */
System.out.println("\n== Class::staticMethod ==");
strList.forEach(MethodReference::staticPrint);
strList.forEach(MethodReference::xxx);
/* compile success?? */
strList.forEach(MethodReference::yyy);
這裡編譯也成功,參數數目對了,但是方法回傳值不為 void
那 lambda 不行,但是 method reference 卻可以
是什麼原因呢?
因為 method 的 signature 不包括 return type ?
/* Error: incompatible types: invalid method reference */
// strList.forEach(MethodReference::zzz);
// strList.forEach(MethodReference::www);
這裡也會編譯錯誤,因為 Consumer 的 function descriptor
參數列表只允許一個引數傳入
/* object::instanceMethod */
System.out.println("\n== object::instanceMethod ==");
MethodReference ref = new MethodReference();
strList.forEach(ref::instancePrint);
/**** Class::instanceMethod ****/
System.out.println("\n== Class::instanceMethod ==");
strList.forEach(System.out::print);
}
private static void staticPrint(String s) {
System.out.print(s + ",");
}
private void instancePrint(String s) {
System.out.print(s + ",");
}
private static void xxx(String s) {
}
private static String yyy(String s) {
return (s == null ? "" : s) + "...";
}
private static void zzz() {
}
private static void www(String s1, String s2) {
}
}
作者: pttworld (批踢踢世界)   2019-08-21 16:18:00
C_AND_CPP板至底文有一卡車貼程式碼網站
作者: ssccg (23)   2019-08-21 18:33:00
Java8的這些新增功能是設計在與舊有type相容的原則上,提供function type寫法的支援,所以才用了functional interface這個其實只是interface,不是新創一種function type的做法好處就是讓舊有的API有可能無痛升級lambda expression和method reference都可以evaluate成functional interface instance,但定義還是不一樣JLS在這兩種expression的type定義,前者在需要void回傳時lambda必須是statement或void-compatible block但後者在需要void的時候,不管reference method的回傳type理由大概也是讓舊有method能盡量無痛拿來reference但是lambda expression是全新的所以適合較嚴格的限制target type是看用在什麼地方,那個地方需要什麼type就會是什麼type,因為目的就是模擬function type而不管是哪個interface type在function type的概念上,API參數宣告成Consumer<String>是代表需要一個String → void,是不是Consumer不重要以你的例子來說,假設有個method是void test(Gg gg)可以 dishes.filter(d -> d.getCalories() < 400);也可以 test(d -> d.getCalories() < 400);d -> d.getCalories() < 400這個lambda "expression"本身是沒有固定是哪個Interface type,是看用在哪就是哪個type你下面的例子不行是因為d -> true這個lambda expression是用在一個assignment statement要求type是Gg這個Interface不是因為filter()不能接受d -> true在API參數中用FunctionalInterface是描述需要的function而不是需要的type,這是functional programming的精神雖然受限FunctionalInterface實作,先把lambda expression決定成某個Interface後就不能再變了,但平常寫程式lambdaexpression通常是直接用在method invoke的參數

Links booklink

Contact Us: admin [ a t ] ucptt.com