Gangmax Blog

自由之思想,独立之精神

Lambda Expression in Java 8

| Comments

编程中使用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
// 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
// 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
// 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