Skip to content

Commit 98a085c

Browse files
authored
JDK8 Lambda表达式
0.0.1 SNAPSHOT
1 parent 72050e6 commit 98a085c

File tree

1 file changed

+133
-1
lines changed

1 file changed

+133
-1
lines changed

Java/What's New in JDK8/Lambda表达式.md

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,136 @@ public class VaraibleHide {
100100
<br>lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,
101101
同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该
102102
不难理解。
103-
## 5.[方法引用](../方法引用.md)
103+
## 5.方法引用
104+
**只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。**
105+
<br>下面的例子会打印list中的每个元素:
106+
```java
107+
List<Integer> list = new ArrayList<>();
108+
for (int i = 0; i < 10; ++i) {
109+
list.add(i);
110+
}
111+
list.forEach(System.out::println);
112+
```
113+
其中```System.out::println```这个就是一个方法引用,等价于Lambda表达式 ```(para)->{System.out.println(para);}```
114+
<br>我们看一下List#forEach方法 ```default void forEach(Consumer<? super T> action)```可以看到它的参数是一个Consumer接口,该接口是一个函数式接口
115+
```java
116+
@FunctionalInterface
117+
public interface Consumer<T> {
118+
void accept(T t);
119+
```
120+
大家能发现这个函数接口的方法和```System.out::println```有什么相似的么?没错,它们有着相似的参数列表和返回值。
121+
<br>我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用
122+
```java
123+
public class MethodReference {
124+
public static void main(String[] args) {
125+
List<Integer> list = new ArrayList<>();
126+
for (int i = 0; i < 10; ++i) {
127+
list.add(i);
128+
}
129+
list.forEach(MethodReference::myPrint);
130+
}
131+
132+
static void myPrint(int i) {
133+
System.out.print(i + ", ");
134+
}
135+
}
136+
137+
输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
138+
```
139+
可以看到,我们自己定义的方法也可以当做方法引用。
140+
<br>到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。
141+
- 方法引用
142+
- 类名::静态方法名
143+
- 类名::实例方法名
144+
- 类名::new (构造方法引用)
145+
- 实例名::实例方法名
146+
可以看出,方法引用是通过(方法归属名)::(方法名)来调用的。通过上面的例子已经讲解了一个`类名::静态方法名`的使用方法了,下面再依次介绍其余的几种
147+
方法引用的使用方法。<br>
148+
**类名::实例方法名**<br>
149+
先来看一段代码
150+
```java
151+
String[] strings = new String[10];
152+
Arrays.sort(strings, String::compareToIgnoreCase);
153+
```
154+
**上面的String::compareToIgnoreCase等价于(x, y) -> {return x.compareToIgnoreCase(y);}**<br>
155+
我们看一下`Arrays#sort`方法`public static <T> void sort(T[] a, Comparator<? super T> c)`,
156+
可以看到第二个参数是一个Comparator接口,该接口也是一个函数式接口,其中的抽象方法是`int compare(T o1, T o2);`,再看一下
157+
`String#compareToIgnoreCase`方法,`public int compareToIgnoreCase(String str)`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它
158+
的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样?
159+
<br>是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是
160+
instance.method(x)中的instance,
161+
它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare(T o1, T o2)`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的
162+
参数。我们再举一个自己实现的例子:
163+
```java
164+
public class MethodReference {
165+
static Random random = new Random(47);
166+
public static void main(String[] args) {
167+
MethodReference[] methodReferences = new MethodReference[10];
168+
Arrays.sort(methodReferences, MethodReference::myCompare);
169+
}
170+
int myCompare(MethodReference o) {
171+
return random.nextInt(2) - 1;
172+
}
173+
}
174+
```
175+
上面的例子可以在IDE里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。
176+
<br>**构造器引用**<br>
177+
构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE会提示String不是一个函数式接口
178+
```java
179+
//compile error : String is not a functional interface
180+
String str = String::new;
181+
```
182+
下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。
183+
```java
184+
interface IFunctional<T> {
185+
T func();
186+
}
187+
188+
public class ConstructorReference {
189+
190+
public ConstructorReference() {
191+
}
192+
193+
public static void main(String[] args) {
194+
Supplier<ConstructorReference> supplier0 = () -> new ConstructorReference();
195+
Supplier<ConstructorReference> supplier1 = ConstructorReference::new;
196+
IFunctional<ConstructorReference> functional = () -> new ConstructorReference();
197+
IFunctional<ConstructorReference> functional1 = ConstructorReference::new;
198+
}
199+
}
200+
```
201+
下面是一个JDK官方的例子
202+
```java
203+
public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>
204+
DEST transferElements(
205+
SOURCE sourceCollection,
206+
Supplier<DEST> collectionFactory) {
207+
208+
DEST result = collectionFactory.get();
209+
for (T t : sourceCollection) {
210+
result.add(t);
211+
}
212+
return result;
213+
}
214+
215+
...
216+
217+
Set<Person> rosterSet = transferElements(
218+
roster, HashSet::new);
219+
```
220+
221+
**实例::实例方法**
222+
<br>
223+
其实开始那个例子就是一个实例::实例方法的引用
224+
```java
225+
List<Integer> list = new ArrayList<>();
226+
for (int i = 0; i < 10; ++i) {
227+
list.add(i);
228+
}
229+
list.forEach(System.out::println);
230+
```
231+
其中System.out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。
232+
## 总结
233+
Lambda表达式是JDK8引入Java的函数式编程语法,使用Lambda需要直接或者间接的与函数式接口配合,在开发中使用Lambda可以减少代码量,
234+
但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda会使代码的可读性急剧下降,并且也节省不了多少代码,
235+
所以在实际开发中还是需要仔细斟酌是否要使用Lambda。和Lambda相似的还有JDK10中加入的var类型推断,同样对于这个特性需要斟酌使用。

0 commit comments

Comments
 (0)