Lambda表达式
lambda表达式支持代码块作为参数,lambda表达式允许使用更简洁的代码创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例
1 lambda表达式入门
下面先使用匿名内部类来改写前面介绍的command表达式的例子
package base;
import 命令模式.Command;
import 命令模式.ProcessArray;
public class CommandTest {
public static void main(String[] args) {
ProcessArray pa = new ProcessArray();
int[] target={3,-4,6,4};
//处理数组,具体处理行为,取决于匿名内部类
pa.process(target, new Command() {
@Override
public void process(int[] target) {
int sum=0;
for(int tmp:target){
sum+=tmp;
}
System.out.println("数组元素总和:"+sum);
}
});
}
}
lambda表达式完全可以于简化创建匿名内部类对象,因此可以将上面代码改为如下形式:
package base;
import 命令模式.ProcessArray;
public class CommandTest2 {
public static void main(String[] args) {
ProcessArray pa = new ProcessArray();
int[] array={3,-4,6,4};
//处理数组,具体行为取决于匿名内部类
pa.process(array,(int[] target)->{
int sum=0;
for(int tmp:array){
sum+=tmp;
}
System.out.println("array数组的总和:"+sum);
});
}
}
从上面的语法格式可以看出,lambda表达式的主要作用就是代替匿名内部类的繁琐的语法,它由三部分组成:
1 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个形参,甚至连形参列表的原括号也可以省略
2 箭头(->)。必须由英文划线号和大于号组成。
3 代码块。如果代码块只包含一条语句。lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。lambda表达式代码块只有一条return语句,甚至可以省略return关键字。lambda表达式需要返回值,而它的代码块仅有一条省略了return的语句,lambda表达式会自动返回这条语句的值。
下面示范了几种lambda表达式的简化写法;
package base;
interface Eatable{
void taste();
}
interface Flyable{
void fly(String weather);
}
interface Addable{
int add(int a,int b);
}
public class LambdaQs {
//调用该方法需要Eatable对象
public void eat(Eatable e){
System.out.println(e);
e.taste();
}
//调用该方法需要Flyable对象
public void drive(Flyable f){
System.out.println("我正在驾驶:"+f);
f.fly("碧空如洗的晴日");
}
//调用该方法需要Addable对象
public void test(Addable add)
{
System.out.println("5与3的和为:"+add.add(5,3));
}
public static void main(String[] args) {
LambdaQs lq= new LambdaQs();
//lambda表达式代码块只有一条语句,可以省略花括号
lq.eat(()-> System.out.println("苹果的味道不错"));
//lambda表达式的形参列表只有一个形参,可以省略花括号
lq.drive(weather->{
System.out.println("今天天气是:"+weather);
System.out.println("直升机飞行平稳");
});
//lambda表达式的代码块只有一条语句,可以省略花括号
//代码块中只有一条语句,即使该表达式需要返回值,也可以省略return关键字
lq.test((a,b)->a+b);
}
}
2 Lambda表达式与函数式接口
Lambda表达式的类型,也被称为“目标类型(target type)”,Lambda表达式的目标类型必须是“函数式接口(functional interface)”。函数式接口代表只包含一种方法的抽象接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
如果采用匿名内部类来创建函数式接口的实例,则只需实现一个抽象方法,在这种情况下即可采用Lambda表达式来创建对象,该表达式创建出来的对象的目标类型就是这个函数式接口。
Runnable r=()->{
for(int i=0;i<100;i++){
System.out.println();
}
}
Lambda表达式实现的是匿名的方法–因此只能实现一个特定的函数式接口中唯一的方法。这意味着Lambda 表达式有两种限制
1 Lambda表达式只能为函数式接口创建对象,lambda表达式只能实现一个方法,因此他只能有一个抽象方法的接口(函数式接口创建对象)。
2 Lambda表达式的目标类型必须是明确的函数式接口。
为保证Lambda表达式 的目标类型是一个明确的函数式接口,可以有以下三种常见方式:
- 将lambda表达式赋值给函数式接口类型变量
- 将lambda表达式作为函数式接口类型的参数传给某个方法
- 使用函数式接口对lambda表达式进行强制类型转换
Object obj1=(Runnable)->{
for(int i=0;i<100;i++){
System.out.println();
}
}
java8 4种典型的接口
XxxFunction:这类接口中通常包含一个apply()抽象方法,该方法对参数进行处理、转换(apply()方法处理逻辑由lambda表达式l来实现),然后返回一个新的值。该函数式接口通常用于对指定数据进行转换
XxxConsummer:这类接口中通常包含一个accept()抽象方法。该方法与XxxFunction接口中的方法基本相似,只是该方法不会返回处理结果。
XxxPredicate:这类接口中通常包含一个test()抽象方法,该方法通常用来对参数进行某种判断,然后返回一个boolean值。
XxxSupplier:这个接口中常常包含一个getAsXxx()抽象方法,该方法不需要输入参数,该方法会按照某种逻辑算法返回一个数据。
Lambda表达式与匿名内部类的联系和区别
相同点:
-
lambda表达式和匿名内部类一样,都可以 直接访问“effextively final”的局部变量,以及外部类的成员变量
-
lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法
package base; interface Displayable{ //定义一个抽象方法和默认方法 void display(); default int add(int a,int b){ return a+b; } } public class LambdaAndInner { private int age=12; private static String name="疯狂软件教育中心"; public void test(){ String book="疯狂java 讲义"; Displayable dis=()->{ //访问局部变量 System.out.println("book局部变量为:"+book); //访问外部类实例变量和类变量 System.out.println("外部类age实例变量:"+age); System.out.println("外部类的name类变量:"+name); }; dis.display(); //调用dis对象从接口中继承add默认方法 System.out.println(dis.add(3,5)); } public static void main(String[] args) { LambdaAndInner lambda = new LambdaAndInner(); lambda.test(); } }
lammbda表达式与匿名内部类的区别:
- 匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但lambda表达式只能为函数式接口创建实例;
- 匿名内部类可以为抽象类甚至普通类创建实例;但lambda表达式只能为函数式接口创建实例
- 匿名内部类实现的抽象方法的方法体允许调用接口定义的默认方法,但lambda表达式的代码块不允许。