要正确、熟练运用 Lambda 表达式,我们需要先正确了解 Lambda 表达式的语法。
Lambda 表达式的语法如下:
(parameters) -> expression
或者
(parameters) -> { statements; }
说明:
parameters: 参数列表,可以是空的,或者包含一个或多个参数。
->(箭头符号): 表示 Lambda 表达式的开始,后面可以跟一个表达式或者一段代码块。如果是表达式,那么该表达式的值将作为 Lambda 函数的返回值;如果是代码块,那么代码块中的语句将会被执行,并且需要使用 return 语句显式返回一个值。
expression:一个表达式,该表达式的值将作为 Lambda 表达式的返回值。
statements:一个代码块,需要使用大括号括起来,并且通过 return 语句返回值。
下面通过几个示例分别介绍 Lambda 表达式基本形式以及几种变种。
Lambda 表达式可以不指定任何参数,如下:
Runnable noArguments = () -> System.out.println("Hello World");
上述 Lambda 表达式不包含参数,使用空括号 () 表示没有参数。并且它实现了 Runnable 接口,该接口只有一个 run() 方法,没有参数,返回类型为 void。
Lambda 表达式可以指定一个参数,参数可以通过括号括起来,如下:
ActionListener oneArgument = event -> System.out.println("button clicked"); // 或者 ActionListener oneArgument = (event) -> System.out.println("button clicked");
上述 Lambda 表达式仅仅包含了一个参数 event。如果仅有一个参数,可以省略括号。
Lambda 表达式的主体部分允许指定多条语句,多条语句需要使用大括号 {} 括起来,如下:
Runnable multiStatment = () -> { System.out.println("Hello"); System.out.println(" World"); };
Lambda 表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,代码块需要使用大括号将代码括起来。
Lambda 表达式支持传递多个参数,如下:
BinaryOperator<Long> add = (x, y) -> x + y; // 或者 BinaryOperator<Long> add = (Long x, Long y) -> x + y;
Lambda 表达式也可以表示包含多个参数的方法。上面代码中,并不是将 x 和 y 数字相加,而是创建了一个函数,用来计算 x 和 y 数字相加的结果。变量 add 的类型是 BinaryOperator<Long>,它不是x 和 y 两个数字的和,而是将 x 和 y 两个数字相加的那行代码。
注意:在 Lambda 表达式中,只在某些分支中返回值(其他分支没有返回值)是不合法的,例如:
// 不合法 (int x) -> { if(x >= 0) return 1; } // 合法 (int x) -> { if(x >= 0) { return 1; } return 0; }
目标类型是指 Lambda 表达式所在上下文环境的类型。比如,将 Lambda 表达式赋值给一个局部变量,或传递给一个方法作为参数,局部变量或方法参数的类型就是 Lambda 表达式的目标类型。
Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。例如:
Runnable task = () -> System.out.println("Hello World");
上述代码,编译器可以轻易推断出 Lambda 表达式的目标类型是 Runnable。
目标类型也不是一个全新的概念,Java 中初始化数组时,数组的类型就是根据上下文推断出来的,例如:
String[] array = {"hello", "world"};
在 Java 8 之前,在进行匿名接口实现时,您必须指定要实现的接口。下面是本文开头的匿名接口实现示例:
stateOwner.addStateListener(new StateChangeListener() { public void onStateChange(State oldState, State newState) { // do something with the old and new state. } });
使用 lambda 表达式时,通常可以从周围的代码中推断出类型。例如,可以从 addStateListener() 方法(StateChangeListener 接口上的单个方法)的方法声明中推断出参数的接口类型。这就是所谓的类型推断。编译器通过在其他地方查找参数的类型 —— 在本例中就是方法定义 —— 来推断参数的类型。下面是本文开头的示例,显示了 lambda 表达式中没有提到 StateChangeListener 接口:
stateOwner.addStateListener( (oldState, newState) -> System.out.println("State changed") );
在 lambda 表达式中,参数类型通常也可以推断出来。在上例中,编译器可以从 onStateChange() 方法的声明中推断出参数的类型。因此,参数 oldState 和 newState 的类型是从 onStateChange() 方法的方法声明中推断出来的。