Gangmax Blog

Lambda Expression in Java 8

编程中使用Lambda表达式是典型的函数式编程风格。我的理解是,Java 8增加Lambda表达式的主要目的是简化编程,提高代码的可读性。可以想象:没有Lambda表达式的Java语言里,除了基本类型之外,一切参数都是class,都必须要有对应的class/interface的定义,哪怕只是临时使用的一次性的代码。这种情况会造成大量的boilerplate code,对程序员不够友好。而且单单从可读性来说,Lambda表达式既简洁又容易理解,使得代码的可读性大大增加。

先看一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

// 1. BaseController

public class BaseController {

/**
* Create an ResultResponse instance for the given error/exception.
*
* @param e Error/Exception
* @return ResultResponse instance
*/
protected ResultResponse createErrorResponse(Throwable e) {
ResultResponse response = new ResultResponse();
response.setStatus(ResultStatus.failed);
response.setMessageField(ResultResponseKeys.Error.name(), e.getMessage());
return response;
}
}

// 2. Controller implementation example.

public class OverviewController extends BaseController {
public ResultResponse query(String timeSlot, String[] catalogs) {
try {
ResultResponse resultResponse = new ResultResponse(ResultStatus.success);
for (String catalog : catalogs) {
String result = overviewHandler.query(OverviewCatalog.valueOf(catalog), timeSlot);
resultResponse.setMessageField(catalog, result);
}
return resultResponse;
} catch (Exception e) {
return this.createErrorResponse(e);
}
}

public ResultResponse listCatalog() {
try {
ResultResponse resultResponse = new ResultResponse(ResultStatus.success);
resultResponse.setMessageField("catalogs", this.overviewHandler.getCatalogs());
return resultResponse;
} catch (Exception e) {
return this.createErrorResponse(e);
}
}
}

有如上的这样一组方法,方法的基本逻辑都是做一些操作返回ResultResponse类型的对象,当发生异常时,构建一个表达异常的ResultResponse对象并返回。诚然,我们可以在这种类的每个方法里面,每次都写一个”try… catch…”,但是这样的做法显然不符合DRY原则(Note: Literal repeat is repeat, structural repeat is still repeat. 而解决后者是函数式编程语言的强项)。在函数式编程语言中,解决这个问题很简单:只要定义一个统一处理函数,它接受一个业务函数作为参数,在它的内容里面去执行传入的业务函数并使用”try…catch…”来处理异常情况,然后每一个具体的业务方法提供业务函数的实现,再将业务函数的实现作为参数调用统一处理函数即可。但在Java语言里却很难有这么简洁的实现方法,直到Java支持了Lambda表达式。

我的第一直觉是这么做的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

// 1. Define a "JobExecutor" interface as the lambda expression argument.

public interface JobExecutor<T> {
/**
* Execute the job content and return a ResultResponse instance as result.
*
* @return
* @throws Exception
*/
public T execute() throws Exception;
}

// 2. BaseController

public class BaseController {

/**
* Create an ResultResponse instance for the given error/exception.
*
* @param e Error/Exception
* @return ResultResponse instance
*/
protected ResultResponse createErrorResponse(Throwable e) {
ResultResponse response = new ResultResponse();
response.setStatus(ResultStatus.failed);
response.setMessageField(ResultResponseKeys.Error.name(), e.getMessage());
return response;
}

public ResultResponse execute(JobExecutor<ResultResponse> executor) {
try {
return executor.execute();
} catch (Exception e) {
return this.createErrorResponse(e);
}
}

// 3. Controller implementation example.

public class OverviewController extends BaseController {
public ResultResponse query(String timeSlot, String[] catalogs) {
return this.execute(() -> {
ResultResponse resultResponse = new ResultResponse(ResultStatus.success);
for (String catalog : catalogs) {
String result = overviewHandler.query(OverviewCatalog.valueOf(catalog), timeSlot);
resultResponse.setMessageField(catalog, result);
}
return resultResponse;
});
}

public ResultResponse listCatalog() {
return this.execute(() -> {
ResultResponse resultResponse = new ResultResponse(ResultStatus.success);
resultResponse.setMessageField("catalogs", this.overviewHandler.getCatalogs());
return resultResponse;
});
}
}

代码简洁了很多。代码简洁的意义不仅在于代码量的减少,更在于可读性的增加和可维护性的大大提高。

但改完之后,我觉得还有一个问题,就是新增的”JobExecutor”类。这个接口类本身其实只是提供了一个方法签名。而在Java中使用Lambda表达式的场景里一定会大量用到这样的类。所以感觉上,这种类应该由JDK来提供。我google了一下,果然如此。请参考这里。JDK提供了各种签名的接口以供类似情况的使用,具体请参考这里

最终代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

// 1. BaseController: note the "Supplier" interface.

import java.util.function.Supplier;

public class BaseController {

/**
* Create an ResultResponse instance for the given error/exception.
*
* @param e Error/Exception
* @return ResultResponse instance
*/
protected ResultResponse createErrorResponse(Throwable e) {
ResultResponse response = new ResultResponse();
response.setStatus(ResultStatus.failed);
response.setMessageField(ResultResponseKeys.Error.name(), e.getMessage());
return response;
}

public ResultResponse execute(Supplier<ResultResponse> executor) {
try {
return executor.get();
} catch (Exception e) {
return this.createErrorResponse(e);
}
}
}

// 2. Controller implementation example(no change).

public class OverviewController extends BaseController {
public ResultResponse query(String timeSlot, String[] catalogs) {
return this.execute(() -> {
ResultResponse resultResponse = new ResultResponse(ResultStatus.success);
for (String catalog : catalogs) {
String result = overviewHandler.query(OverviewCatalog.valueOf(catalog), timeSlot);
resultResponse.setMessageField(catalog, result);
}
return resultResponse;
});
}

public ResultResponse listCatalog() {
return this.execute(() -> {
ResultResponse resultResponse = new ResultResponse(ResultStatus.success);
resultResponse.setMessageField("catalogs", this.overviewHandler.getCatalogs());
return resultResponse;
});
}
}

如果非要说Java Lambda表达式的缺点,我想最主要的就是:Java本身是一种vorbose的基于class的静态类型语言,它的Lambda表达式的实现本质上是一种语法糖(syntactic sugar)。为了实现这种语法糖,Java VM做了大量的背后工作(比如”在运行期生成接口的相关实现类“),而这其中的原理并不是一目了然的,这一点对初学者容易造成理解上的困难。相比而言,函数式编程语言中的lambda表达式的原理就很简单且容易理解。不过对Java来说,这也是没有办法的办法吧!

Comments