1
xbigfat 2018-10-24 11:33:59 +08:00 via iPhone
new ArrayList ()实现了 add 方法
|
3
ffeii 2018-10-24 11:37:16 +08:00 via iPhone
List::add 等同于 (list, str) -> list.add(str)
|
4
Cbdy 2018-10-24 11:38:26 +08:00
List::add 你可以理解成为这样一个函数
BiConsumer<List<String>, String> v = (List<String> l, String e) -> l.add(e); |
5
solupro 2018-10-24 11:41:10 +08:00
取决于 List<String>的实现,你这里就是 ArrayList 了呀
|
6
xbigfat 2018-10-24 11:43:38 +08:00
1.泛型去掉后,编译器不知道你输入输出的是什么类型,所以报错。BiConsumer 是输入 T 返回 R,不写出类型是编译器报错,运行时泛型是擦除掉的。
2. new ArrayList( ) 里面,ArrayList 实现了 add ( ) 方法,所以可以运行。 3.你困惑是因为 Lambda 的缩减.将 ```BiConsumer<List<String>, String> v = List::add;``` 替换为: ``` List<String> list = new ArrayList<>(); list.add("hello"); list.add("alex"); list.add("front"); BiConsumer<List<String>, String> v = new BiConsumer<List<String>, String>() { @Override public void accept(List<String> strings, String e) throws Exception { strings.add(e); } }; ``` 这里还原成匿名内部类,能看懂了吗? strings 传递来的是 list 的引用,e 传来的是 “ ddd" |
7
serical 2018-10-24 11:48:29 +08:00 via Android
具体实现取决于 v.accept 第一个参数的类型
|
8
xbigfat 2018-10-24 11:48:46 +08:00
|
9
kuko126 2018-10-24 11:48:55 +08:00 2
方法引用的几种写法,其中有
类名::实例方法名 若 Lambda 表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法 https://blog.csdn.net/TimHeath/article/details/71194938 所以可以从 BiConsumer<List<String>, String> v = (list1, s) -> list1.add(s); 转换成 BiConsumer<List<String>, String> v = List::add; 2 是因为泛型不写默认就是 Object,Object 里没有 add 方法所以编译会报错 可以试一下下面的看一下区别 BiConsumer<List<String>, String> v = (list1, s) -> list1.add(s); BiConsumer<List<String>, String> v = ArrayList::add; BiConsumer v = Object::equals; |
10
yidinghe 2018-10-24 11:50:23 +08:00
这条语句是在声明一个方法引用,而并不是真的调用这个方法。这样理解就知道为什么符合语法了。
|
11
logtheone OP @ffeii
你这个等同有依据么?? List 的 add 的方法签名都和 accept 不一样,怎么等同过来的? |
12
logtheone OP @kuko126
你的这个回答说到点子上了。 再问一句,这句话“若 Lambda 表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法”当 Lambda 表达式有 3 个以上的参数时,还适用么?另外这句话有权威来源么? |
13
kuko126 2018-10-24 14:49:28 +08:00 1
如果有三个及以上参数 就要用 (a, b, c) -> a.func(b, c); 这种形式
要权威的话可以看下这个 https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html |
15
passerbytiny 2018-10-24 18:26:14 +08:00
Lambda 表达式为啥要跟函数式接口一起出现,把我给看晕了。
List::add 的意思,不是调用 List 接口的 add 方法,你这点理解错了,所有后面的理解就走不通了。 A::B 是一种 Lambda 缩写方式,并不是调用 A 的 B 方法,当 Lambda 表达式只有一个语句,并且可以通过函数式接口、A、B,来进行推断的时候,才能使用这种缩写形式。当 A 是类的实例,B 是方法名的时候,看起来可能像是表示调用 A 对象的 B 方法。但当 A 是类,或者 B 是 new 这种特殊字的时候,就不能那么看了。 函数式接口是“ BiConsumer<T, U> void accept(T t, U u)”,上下文语句“ BiConsumer<List<String>, String> v ”决定了:T 是 List<String>、U 是 String。 结合查看 void accept(T t, U u) 和 List::add: accept 提供了两个参数; List.add 方法只需要一个参数; List 是接口定义而不是具体实例。 那么自然的,将第一个参数当成 List.add 方法的执行主体,将第二个参数当成方法的参数,于是还原成了:(arg_List,arg_String)->{arg_List.add(arg_String);} 你也可以这样认为,List::add 定义了一个代表 List.add 方法的 Method,然后执行的时候就是 Method.invoke(obj,args...)。此时,“ v.accept(list, "ddd")”相当于:method.invoke(list,"aaa")。执行的是你通过“ List<String> list = new ArrayList<>();”创建的 list 对象,而不是 List 接口。 |
16
passerbytiny 2018-10-24 18:48:10 +08:00 via Android
忽略我上边的还原过程吧,错了。A::b 表示的就是方法签名,即 A 类 /接口的 b 方法,所以 List::add 就代表 List 接口的 add 方法,然后再结合 accpet ( list, "aaa"),要推断要执行的是:<List>list.add("aaa")。
|
17
passerbytiny 2018-10-24 18:58:19 +08:00 via Android
SomeClass::new,就是该类构造器的方法签名,invoke 的时候不需要第一个参数; someInstance::method,表示方法签名以及 invoke 时的第一个参数。因此,Lambda 表达式右边方法的参数个数,才跟函数式接口方法参数的个数相同。
跟反射机制已对比,容易理解多了 |