作者jtorngl (DDFL)
看板java
标题[问题] method reference
时间Wed Aug 21 14:34:30 2019
最近才开始看 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://webptt.com/cn.aspx?n=bbs/java/M.1566369273.A.05B.html
1F:→ pttworld: C_AND_CPP板至底文有一卡车贴程式码网站 08/21 16:18
2F:→ ssccg: Java8的这些新增功能是设计在与旧有type相容的原则上,提供 08/21 18:33
3F:→ ssccg: function type写法的支援,所以才用了functional interface 08/21 18:33
4F:→ ssccg: 这个其实只是interface,不是新创一种function type的做法 08/21 18:35
5F:→ ssccg: 好处就是让旧有的API有可能无痛升级 08/21 18:37
感谢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
6F:→ ssccg: lambda expression和method reference都可以evaluate成 08/21 18:38
7F:→ ssccg: functional interface instance,但定义还是不一样 08/21 18:39
8F:→ ssccg: JLS在这两种expression的type定义,前者在需要void回传时 08/21 18:48
9F:→ ssccg: lambda必须是statement或void-compatible block 08/21 18:51
10F:→ ssccg: 但後者在需要void的时候,不管reference method的回传type 08/21 18:54
11F:→ ssccg: 理由大概也是让旧有method能尽量无痛拿来reference 08/21 18:55
12F:→ ssccg: 但是lambda expression是全新的所以适合较严格的限制 08/21 18:59
所以答案在 Java Language Specification 有提到
感谢ss大的说明 :)
13F:→ ssccg: target type是看用在什麽地方,那个地方需要什麽type就会是 08/21 19:32
14F:→ ssccg: 什麽type,因为目的就是模拟function type而不管是哪个 08/21 19:33
15F:→ ssccg: interface type 08/21 19:34
16F:→ ssccg: 在function type的概念上,API参数宣告成Consumer<String> 08/21 19:37
17F:→ ssccg: 是代表需要一个String → void,是不是Consumer不重要 08/21 19:37
我现在要用 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 的意思?
18F:→ ssccg: 以你的例子来说,假设有个method是void test(Gg gg) 08/21 22:02
19F:→ ssccg: 可以 dishes.filter(d -> d.getCalories() < 400); 08/21 22:02
20F:→ ssccg: 也可以 test(d -> d.getCalories() < 400); 08/21 22:03
21F:→ ssccg: d -> d.getCalories() < 400这个lambda "expression"本身是 08/21 22:03
22F:→ ssccg: 没有固定是哪个Interface type,是看用在哪就是哪个type 08/21 22:04
23F:→ ssccg: 你下面的例子不行是因为d -> true这个lambda expression是 08/21 22:05
24F:→ ssccg: 用在一个assignment statement要求type是Gg这个Interface 08/21 22:05
25F:→ ssccg: 不是因为filter()不能接受d -> true 08/21 22:06
26F:→ ssccg: 在API参数中用FunctionalInterface是描述需要的function而 08/21 22:07
27F:→ ssccg: 不是需要的type,这是functional programming的精神 08/21 22:08
28F:→ ssccg: 虽然受限FunctionalInterface实作,先把lambda expression 08/21 22:10
29F:→ ssccg: 决定成某个Interface後就不能再变了,但平常写程式lambda 08/21 22:11
30F:→ ssccg: expression通常是直接用在method invoke的参数 08/21 22:12
以 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