[問題] method reference
最近才開始看 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) {
}
}
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.37.155.168 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/java/M.1566369273.A.05B.html
→
08/21 16:18,
5年前
, 1F
08/21 16:18, 1F
→
08/21 18:33,
5年前
, 2F
08/21 18:33, 2F
→
08/21 18:33,
5年前
, 3F
08/21 18:33, 3F
→
08/21 18:35,
5年前
, 4F
08/21 18:35, 4F
→
08/21 18:37,
5年前
, 5F
08/21 18:37, 5F
感謝ss大,在看 lambda 前有看到 why lambda 的文章
該文舉了一個 filter apple 的例子,若新增需求,要一直新增或修改方法
透過設計的方式,有點像是用了 strategy pattern 來處理
然後使用 anonymous class 去解決,這是在 lambda 之前
然後 lambda 可以寫出更簡潔,更短的程式碼
是的,我看好像大部份介紹 lambda 提到的好處都是這樣
但是在後續的 Stream 會提到,並行處理,多執行緒處理等
換言之,導入 lambda 應該也是為了解決某些效能上的問題
不過現在頭痛的就是,java.util.function 是有定義好一些函式介面
但若我也定義了自己的函式介面,lambda 的 target type 是哪個不知道
雖說 java 8 設計的應該足以使用,不太需要自己定義函式介面了
然後看 Stream 時更亂了,一堆 lambda
然後我只能去看方法的參數到底是什麼型別
也就是目前如果要使用,我不知道如何快速的去應用這些 API
→
08/21 18:38,
5年前
, 6F
08/21 18:38, 6F
→
08/21 18:39,
5年前
, 7F
08/21 18:39, 7F
→
08/21 18:48,
5年前
, 8F
08/21 18:48, 8F
→
08/21 18:51,
5年前
, 9F
08/21 18:51, 9F
→
08/21 18:54,
5年前
, 10F
08/21 18:54, 10F
→
08/21 18:55,
5年前
, 11F
08/21 18:55, 11F
→
08/21 18:59,
5年前
, 12F
08/21 18:59, 12F
所以答案在 Java Language Specification 有提到
感謝ss大的說明 :)
→
08/21 19:32,
5年前
, 13F
08/21 19:32, 13F
→
08/21 19:33,
5年前
, 14F
08/21 19:33, 14F
→
08/21 19:34,
5年前
, 15F
08/21 19:34, 15F
→
08/21 19:37,
5年前
, 16F
08/21 19:37, 16F
→
08/21 19:37,
5年前
, 17F
08/21 19:37, 17F
我現在要用 lambda 都必須先看 target type 的 function descriptor
例如 Predicate<T> 的 abstract method 為 boolean test(T t);
所以 function descriptor 是 T -> boolean
但是後面提到的,是不是 Consumer 不重要,這我看不懂
List<Dish> menuList = ...................
Stream<Dish> dishes = menuList.stream();
dishes.filter(d -> d.getCalories() < 400);
但我不能這樣寫啊
@FunctionalInterface
interface Gg {
boolean gg(Dish d);
}
Gg gg = d -> true;
dishes.filter(gg);
當然,這個例子蠻白痴的,我要用 API 本來就要知道他吃什麼參數
所以我不懂ss大說的,是不是 Consumer 不重要,是什麼意思?
或者是,我的腦袋根本還不知道 functional programming 的意思?
→
08/21 22:02,
5年前
, 18F
08/21 22:02, 18F
→
08/21 22:02,
5年前
, 19F
08/21 22:02, 19F
→
08/21 22:03,
5年前
, 20F
08/21 22:03, 20F
→
08/21 22:03,
5年前
, 21F
08/21 22:03, 21F
→
08/21 22:04,
5年前
, 22F
08/21 22:04, 22F
→
08/21 22:05,
5年前
, 23F
08/21 22:05, 23F
→
08/21 22:05,
5年前
, 24F
08/21 22:05, 24F
→
08/21 22:06,
5年前
, 25F
08/21 22:06, 25F
→
08/21 22:07,
5年前
, 26F
08/21 22:07, 26F
→
08/21 22:08,
5年前
, 27F
08/21 22:08, 27F
→
08/21 22:10,
5年前
, 28F
08/21 22:10, 28F
→
08/21 22:11,
5年前
, 29F
08/21 22:11, 29F
→
08/21 22:12,
5年前
, 30F
08/21 22:12, 30F
以 Stream<T> 的 filter(Predicate<? super T> predicate)
因為 Predicate 的 function descriptor 是 T -> boolean
所以我在使用 filter() 時
d -> d.getCalories() < 400 也可以
d -> d.getType == Dish.Type.MEAT 也可以
d -> true 也可以
它們都符合 T -> boolean
使用 interface 就是讓我們注意在邏輯的實做就好
而不是想著要把它們歸屬於某個 type (某個 class)
就像我宣告了一個 Gg 的 functional interface
它的 functional descriptor 和 Predicate 一樣
但是我的思維是,用 Gg 解決某一個需求
下次遇到另一個需求,又宣告另一個 Yy 來解另一個需求
雖然程式是在用 lambda expression
但思維和原本的,遇到一個需求就增加一個 method 沒兩樣
也就是說 Predicate<T> 允許各種符合 function descriptor 的實作
我只要關注在,我的實做邏輯是什麼
然後用 lambda expression 丟進去就對了
這種理解不知道是否正確?
或是簡單說,我的 design pattern 的功力太弱
思維總是不夠抽象,總是對 concreat class 寫程式這樣對吧?
還是要感謝ssccg大的解說就是了 :)
※ 編輯: jtorngl (114.37.155.168 臺灣), 08/21/2019 22:54:45
java 近期熱門文章
PTT數位生活區 即時熱門文章
-2
11