目录
面对大型数据集合,Java还欠缺高效的并行操作,因此Java 8增加了Lambda表达式。Lambda表达式能够使开发者简单高效地开发出在多核CPU上高效运行的代码。Lambda表达式是函数式编程,我们知道面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。那什么是函数式编程?函数式编程的核心思想是在思考问题时,使用不可变值和函数, 函数对一个值进行处理映射成另外的一个值。
1.什么是Lambda表达式?
Java8的最大变化是引入了Lambda表达式,一种紧凑的、传递行为的方式。下面的例子中,我们创建了一个新对象,它是实现了ActionListener接口。 为了调用一行重要的逻辑代码,不得不加上4行冗余的样板代码:
Runnable old = new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
};
如果我们不想传入对象,只想传入行为,可以使用Lambda表达式实现:
Runnable noArguments = () -> System.out.println("Hello World");
->将参数和Lambda表达式的主体分开,而主体是用户点击按钮时会运行的一些代码。在Lambda表达式中无需指定类型,程序依然可以编译。这是因为Javac 根据程序的上下文,在后台推断了参数类型。这意味着如果参数类型不言而明,则无需显式指定。有时,编译器不一定能根据上下文推断出参数的类型,此时 我们声明参数时就必须包含类型信息。
2. 如何辨别Lambda表达式
Lambda表达式除了基本的形式外还有以下几种形式:
1 Runnable noArguments = () -> System.out.println("Hello World");
2 ActionListener oneArgument = event -> System.out.println("Hello World");
3 Runnable multiStatement = () -> {
System.out.println("Hello");
System.out.println("World");
};
4 BinaryOperator<Long> add = (x, y) -> x + y;
5 BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;
- 1中Lambda表达式不包含参数,使用空括号()表示没有参数
- 2中所示的Lambda表达式包含且只包含一个参数,可省略参数的括号
- 3中所示Lambda表达式的主体不仅可以是一个表达式,而且也是一段代码块,使用大括号({})将代码块括起来
- 4中表示Lambda表达式也可以表示包含多个参数的方法,这时就有必要思考怎样去阅读该Lambda表达式。这行代码并不是将两个数字相加,而是创建了一个函数
用来计算两个数字相加的结果。
以上四种Lambda表达式中的参数类型都是由编译器推断得出的,但有时最好也可以显式的声明参数类型,此时就需要使用小括号将参数括起来,多个参数的情况
也是如此。
3.引用值而不是变量
如果你曾使用过匿名内部类, 也许遇到过这样的情况: 需要引用它所在方法里的变量。 这时,需要将变量声明为 final,如下代码所示:
final String name = getUserName();
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.out.println("hi " + name);
}
});
将变量声明为 final, 意味着不能为其重复赋值,同时也意味着在使用 final 变量时,实际上是在使用赋给该变量的一个特定的值。Java 8中虽然放松了这一限制,可以用非final的变量,但是该变量在既定事实上必须是final,否则编译器就会报错。因此Lambda表达式中引用的局部变量也必须是final或者是既成事实上的final变量。这种行为也解释了为什么Lambda表达式被称为是闭包。未赋值的变量与周边魂晶隔离起来,进而被绑定到一个特定的值。Lambda表达式都是静态类型的,因此,接下来分析一下Lambda表达式本身的类型:函数接口。
Lambda表达式示例代码如下:
/**
* 使用Lambda表达式的必要条件:
* 1.必须有一个接口
* 2.接口当中必须保证有且仅有一个抽象方法
* FunctionalInterface注解用来检测是否是函数式接口,当不是函数式接口时,编译失败
*
* @author: xuzongxin
* @date: 2019/7/16 14:38
* @description:
*/
@FunctionalInterface
public interface Bird {
//唯一抽象方法
void sing();
}
测试实现代码:
public class BirdTest {
public static void method(Bird bird) {
bird.sing();
}
public static void main(String[] args) {
method(() -> {
System.out.println("bling bling...");
});
}
}
5.函数接口
函数接口是只有一个抽象方法的接口,用作Lambda表达式的类型。Java中的重要的函数接口主要如下:
大多数情况下,省略类型信息可以减少干扰,使得代码更加的简洁,因此在Java8中,程序员可以省略Lambda表达式中的所有参数类型。javac根据Lambda表达式上下文信息就能推断出参数的正确类型。程序依然要经过类型检查来保证运行的安全性,但不用再显式声明类型罢了。 这就是所谓的类型推断。
Predicate<Integer> atLeast5 = x -> x > 5;
Predicate是一个具有返回值的Lambda表达式,Lambda表达式实现了Predicate接口,因此它的单一参数被推断为Integer 类型,如果我们把Integer去掉就会编译报错。javac还可检查Lambda表达式的返回值是不是boolean,这正是Predicate方法的返回类型。
6.总结
• Lambda 表达式是一个匿名方法, 将行为像数据一样进行传递。
• Lambda 表达式的常见结构: BinaryOperator<Integer> add = (x, y) → x + y。
• 函数接口指仅具有单个抽象方法的接口, 用来表示 Lambda 表达式的类型。