Java Lambda 表达式初见

1.Lambda 表达式的背景

Lambda 表达式是从 Java8 开始推出的功能,这个强大的功能弥补了 Java 在函数式编程方面的缺陷。Lambda 表达式看似是一个很普通的功能,其实背后隐藏了很深的编程思想。

要想深刻理解 Lambda 表达式,首先应该了解函数式编程 ( functional programming ),有关函数式编程的大致思想,可以参考这篇文章,此处不做解释。

2.Lambda 表达式的用法

在学习 Lambda 表达式用法之前,需要先明确一个概念,即函数式接口。所谓的函数式接口,其实就是只拥有一个方法的接口,这种接口之前被称为 SAM 类型 ( Single Abstract Method ) 。理解了函数式接口之后便可以开始学习 Lambda 表达式的使用方式。

下面是一些 Lambda 表达式:

() -> "lambda"
(int a, int b) -> a+b
(String s) -> { System.out.println(s); }

第一个表达式不接收参数,返回字符串 "lambda",第二个表达式接受两个 int 参数 a 和 b,返回两者的和,第三个表达式接受一个字符串并打印,不返回值。

Lambda 表达式主要分为三个部分:参数列表箭头函数体。函数体可以是一个单表达式或者一个语句块。如果是单表达式则表达式会被执行然后返回结果,可以省略 return 关键字。如果是一个语句块,则语句块就会像普通方法中的语句块一样被执行。

观察下面两个表达式:

() -> "lambda"
() -> { return "lambda"; }

由于第二个表达式是语句块,所以必须带 return,即使它里面只有一句话。当然在实际应用中并不会写像第二句这样的表达式,如果你写了,你的 IDE 估计也会提醒你可以使用表达式 Lambda 来代替语句块 Lambda。

3.Lambda 表达式的使用时机

简单了解了 Lambda 表达式的语法,那么它使用的地方又是在哪里呢?如果只是简单的将上面的表达式敲到程序里,你应该得不到你想要的结果。

其实 Lambda 表达式可以看作是匿名内部类的一种延申,观察下面的例子:

/* TestInter.java */
public interface TestInter {
    String getMessage();
}

/* TestPrint.java */
public class TestPrint {
    void print(TestInter test) {
        System.out.println(test.getMessage());
    }
}

如果是使用匿名内部类,那么在调用 TestPrint 类的 print() 时一般会像下面这样写:

/* Test.java */
public class Test {
    public static void main(String[] args) {
        TestPrint testPrint = new TestPrint();
        testPrint.print(new TestInter() {
            @Override
            public String getMessage() {
                return "lambda";
            }
        });
    }
}

如果你使用的 IDE 比较新,可能已经注意到 IDE 提示你在匿名内部类处可以使用 Lambda 表达式代替。是的,上面的类可以通过使用 Lambda 表达式改写成下面的样子:

/* Test.java */
public class Test {
    public static void main(String[] args) {
        TestPrint testPrint = new TestPrint();
        testPrint.print(() -> "lambda");
    }
}

可以看到,通过使用 Lambda 表达式,将一个十一行的类缩减到了六行。

前面提到了函数式接口,可以看到上面的例子中 TestInter 就是一个函数式接口。当我们在用匿名内部类实现函数式接口的时候,就可以选择使用 Lambda 表达式代替匿名内部类。Lambda 表达式并不仅仅起到一个代码精简的作用,关于更深层次的作用本文不做探讨。

4.Lambda 表达式的类型

到此我们对 Lambda 表达式已经有了一个大概的了解,那么也应该会注意到一个问题,我们没有指定 Lambda 表达式的类型。在第三节的例子中,我们并没有指定 Lambda 表达式的类型,但是编译器通过上下文推导,推导出该 Lambda 表达式的类型为接口 TestInter,这个被推导出的类型称为 Lambda 表达式的目标类型,理解了目标类型的概念,我们就能更近一步地定义 Lambda 表达式的使用时机了。Lambda 表达式只能出现在目标类型为函数式接口的上下文中

既然 Lambda 表达式是用来实现函数式接口的,那么对于接口方法的参数类型其实也可以推导出来,是不是也可以省略参数类型呢?答案是肯定的。例如我有这样一个接口:

public interface Test {
    void testFunc(String a, String b);
}

那么我就可以像下面这样用 Lambda 表达式定义实例:

Test test = (x, y) -> x.compareTo(y);

特别的,当参数只有一个的时候,还可以省略参数两边的括号,就像下面这样:

x -> x == 1

5.结尾

该文章只是作为 Lambda 表达式的一个初见,关于 Lambda 表达式还有很多细节可以探讨,这些细节将会在之后的文章中出现。作为学习 Lambda 表达式的随笔,该文章肯定还有很多不足之处,欢迎指出交流。

参考文档:

docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.htm

你可能感兴趣的:(Java)