这是我参与8月更文挑战的第11天
为什么要使用lambda表达式
Java 对象往往比较“重量级”:实例化一个类型往往会涉及不同的类,并需要初始化类里的字段和方法。
不过有些 Java 对象只是对单个函数的封装。例如下面这个典型用例:Java API 中定义了一个接口(一般被称为回调接口),用户通过提供这个接口的实例来传入指定行为,例如:
public interface ActionListener {
void actionPerformed(ActionEvent e);
}
因为该方法只在调用处使用一次,所以我们并不需要去实现这个类,在java8之前,我们一般都是使用匿名内部类的方式去实现该方法:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.dazzle(e.getModifiers());
}
});
缺点:
- 语法过于冗余
- 匿名类中的 this 和变量名容易使人产生误解
- 类型载入和实例创建语义不够灵活
- 无法捕获非 final 的局部变量
- 无法对控制流进行抽象
因此,jkd8中引入lambda表达式很好的解决了这一个问题
lambda表达式的引入
对接口的要求:
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用
lambda表达式语法:
(参数列表)->{方法体}
其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符
lambda表达式强调可推导、可省略,所以见到大部分lambda表达式都是 变量->方法体 或 类名::方法
lambda表达式结构:
下面是一些lambda表达式实例:
(int x, int y) -> {return x + y;}
() -> {return 42;}
(String s) -> { System.out.println(s); }
不讨论方法及接口名,上述表达式对应java8之前代码为:
int 方法名 (int x, int y){
return x+y;
}
int 方法名 (){
retrun 42;
}
void 方法名(String s){
System.out.println(s);
}
如果方法体只有一条语句时,可以省略大括号,上述表达式为:
(int x, int y) -> return x + y;
() -> return 42;
(String s) -> System.out.println(s);
如果方法体只有一条语句且是return语句时,可以省略retrun和分号,优化过的表达式为:
(int x, int y) -> x + y
() -> 42
(String s) -> System.out.println(s);
return和分号必须一起省略
是不是感觉清晰了很多,但这只是lambda表达式最基本的表达形式,lambda可以通过目标类型的上下文来推导数据类型,比方说上述的第一个方法就可以简化为:
(x,y)->x+y
如果数据类型不写,则所有的变量都不写,不能写作(x,int y)->x+y这种。
当参数列表有且只有一个参数时,参数括号可以省略,例如
x->x+2
Lambda 表达式常用示例
有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。
语法:
目标引用::方法 静态方法的归属者为类名,普通方法归属者为对象
双冒号 :: person::getName(),等同于person.getName(),表示对象引用方法