一、lamda表达式的优点
Lambda表达式(闭包):java8的新特性,lambda运行将函数作为一个方法的参数,也就是函数作为参数传递到方法中。使用lambda表达式可以让代码更加简洁。
Lambda表达式的使用场景:用以简化接口实现。
关于接口实现,可以有很多种方式来实现。例如:设计接口的实现类、使用匿名内部类。 但是lambda表达式,比这两种方式都简单。比较以下两种实现方式:
public interface SwimInterface {
String swim(int duration);
}
实现方式一:
public static void main(String[] args) {
SwimInterface freeStyle = new SwimInterface() {
@Override
public String swim(int duration) {
return "游泳方式为自由泳,时长:" + duration;
}
};
SwimInterface butterfly = new SwimInterface() {
@Override
public String swim(int duration) {
return "游泳方式为蝶泳,时长:" + duration;
}
};
SwimInterface breaststroke = new SwimInterface() {
@Override
public String swim(int duration) {
return "游泳方式为蛙泳,时长:" + duration;
}
};
String swim = freeStyle.swim(1);
String swim1 = butterfly.swim(2);
String swim2 = breaststroke.swim(3);
System.out.println(swim);
System.out.println(swim1);
System.out.println(swim2);
}
实现方式二:
public static void main(String[] args) {
SwimInterface freeStyleFuntion = (d) -> "游泳方式为自由泳,时长:" + d;
SwimInterface butterflyFuntion = (d) -> "游泳方式为蝶泳,时长:" + d;
SwimInterface breaststrokeFuntion = (d) -> "游泳方式为蛙泳,时长:" + d;
String swim = freeStyleFuntion.swim(1);
String swim1 = butterflyFuntion.swim(2);
String swim2 = breaststrokeFuntion.swim(3);
System.out.println(swim);
System.out.println(swim1);
System.out.println(swim2);
}
分别使用匿名内部类和lamda表达式实现同一个接口,显而易见,lamda表达式实现方式更加简洁。
二、Lambda表达式的语法
- lambda表达式,其实本质来讲,就是⼀个匿名函数,从上面的例子也可以看出来,匿名内部类转成lamda表达式。因此在写lambda表达式的时候,不需要关心方法名是什么。实际上,我们在写lambda表达式的时候,也不需要关心返回值类型。
我们在写lambda表达式的时候,只需要关注两部分内容即可:参数列表和方法体
lambda表达式的基础语法:
(参数1,参数2,…) -> {
方法体
};
-
参数部分:方法的参数列表,要求和实现的接口中的方法参数部分⼀致,包括参数的数量和类型。
-
方法体部分 : 方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。
-
-> : 分隔参数部分和方法体部分。
代码示例:
SwimInterface backstroke = (int d) -> {
System.out.println("我学会仰泳了");
return "游泳方式为仰泳,时长:" + d;
};
backstroke.swim(100);
- 参数的小括号
如果方法的参数列表中的参数数量 有且只有⼀个,此时,参数列表的小括号是可以省略不写的。
SwimInterface backstroke = d -> {
System.out.println("我学会仰泳了");
return "游泳方式为仰泳,时长:" + d;
};
backstroke.swim(100);
注意事项:
只有当参数的数量是⼀个的时候, 多了、少了都不能省略。
省略掉小括号的同时, 必须要省略参数的类型。
- 方法体部分的精简
当⼀个方法体中的逻辑,有且只有⼀句的情况下,⼤括号可以省略;
如果⼀个方法中唯⼀的⼀条语句是⼀个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。
SwimInterface breaststrokeFuntion = d -> "游泳方式为蛙泳,时长:" + d;
String swim2 = breaststrokeFuntion.swim(3);
三、函数式接口
在Java中,函数式接口是指仅包含一个抽象方法的接口,它可以被隐式转换为一个 lambda 表达式或者方法引用。函数式接口的主要用途是支持函数式编程,使得代码更加简洁和易于阅读。
特点:
- 单一抽象方法:函数式接口只能有一个未实现的方法,这使得它可以表示单一的行为。
- 默认方法:虽然只能有一个抽象方法,但函数式接口可以包含多个默认方法和静态方法。
- @FunctionalInterface 注解:使用 @FunctionalInterface 注解可以清晰地表明一个接口是函数式接口,编译器会强制检查是否满足函数式接口的条件,就像@override注解一样,但不是强制的。
- lamda表达式中参数和返回值必须跟接口中定义的抽象方法一致。
@FunctionalInterface
public interface MyFunctionalInterface {
void execute();
default void defaultMethod() {
System.out.println("This is a default method.");
}
static void staticMethod() {
System.out.println("This is a static method.");
}
}
可以使用 lambda 表达式来实现这个函数式接口:
public class Main {
public static void main(String[] args) {
MyFunctionalInterface myFunc = () -> System.out.println("Executing...");
myFunc.execute(); // 输出: Executing...
myFunc.defaultMethod(); // 输出: This is a default method.
MyFunctionalInterface.staticMethod(); // 输出: This is a static method.
}
}
四、函数引用
函数引用:引用⼀个已经存在的方法,使其替代lambda表达式完成接口的实现。
lambda表达式是为了简化接口的实现的。在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。如果在lambda表达式中需要处理的逻辑比较复杂,⼀般情况会单独的写⼀个方法。在lambda表达式中直接引用这个方法即可。
4.1.静态方法的引用
语法:类::静态方法
注意事项:
在引用的方法后面,不要添加小括号。
引用的这个方法,参数(数量、类型)和返回值,必须要跟接口中定义的⼀致
public class Test05 {
public static void main(String[] args) {
//实现多个参数,一个返回值的接口
//对一个静态方法的引用,语法:类::静态方法
TestInterface testInterface = Calculator::calculate;
System.out.println(testInterface.test(4,5));
}
}
class Calculator{
public static int calculate(int a,int b ){
// 稍微复杂的逻辑:计算a和b的差值的绝对值
if (a > b) {
return a - b;
}
return b - a;
}
}
interface TestInterface{
int test(int a,int b);
}
4.2 非静态方法的引用
语法:对象::非静态方法
注意事项:
在引用的方法后⾯,不要添加小括号。
引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的⼀致。
public class Test06 {
public static void main(String[] args) {
//对非静态方法的引用,需要使用对象来完成
Calculator cc = new Calculator();
TestInterface testInterface = cc::calculate;
System.out.println(testInterface.test(2, 3));
}
private static class Calculator{
public int calculate(int a, int b) {
return a > b ? a - b : b - a;
}
}
}
interface TestInterface{
int test(int a,int b);
}
3.构造方法的引用
使用场景
如果某⼀个函数式接口中定义的方法,仅仅是为了得到⼀个类的对象。此时我们就可以使用构造方法的引用,简化这个方法的实现。
语法:类名::new
注意事项:可以通过接口中的方法的参数, 区分引用不同的构造方法。
public class Test {
private static class Dog{
String name;
int age;
//无参构造
public Dog(){
System.out.println("一个Dog对象通过无参构造被实例化了");
}
//有参构造
public Dog(String name,int age){
System.out.println("一个Dog对象通过有参构造被实例化了");
this.name = name;
this.age = age;
}
}
//定义一个函数式接口,用以获取无参的对象
@FunctionalInterface
private interface GetDogInterface{
//若此方法仅仅是为了获得一个Dog对象,而且通过无参构造去获取一个Dog对象作为返回值
Dog test();
}
//定义一个函数式接口,用以获取有参的对象
@FunctionalInterface
private interface GetDogWithParamInterface{
//若此方法仅仅是为了获得一个Dog对象,而且通过有参构造去获取一个Dog对象作为返回值
Dog test(String name,int age);
}
// 测试
public static void main(String[] args) {
//lambda表达式实现接口
GetDogInterface lm = Dog::new; //引用到Dog类中的无参构造方法,获取到一个Dog对象
Dog dog = lm.test();
System.out.println("修狗的名字:"+dog.name+" 修狗的年龄:"+dog.age); //修狗的名字:null 修狗的年龄:0
GetDogWithParamInterface lm2 = Dog::new;//引用到Dog类中的有参构造,来获取一个Dog对象
Dog dog1 = lm2.test("萨摩耶",2);
System.out.println("修狗的名字:"+dog1.name+" 修狗的年龄:"+dog1.age);//修狗的名字:萨摩耶 修狗的年龄:2
}
}
五、jav8 Function包
使用注解@FunctionalInterface标识,并且只包含一个抽象方法的接口是函数式接口。函数式接口主要分为Supplier供给型函数、Consumer消费型函数、Runnable无参无返回型函数和Function有参有返回型函数。
5.1 Supplier供给型函数
Supplier的表现形式为不接受参数、只返回数据
5.2 Consumer消费型函数
Consumer消费型函数和Supplier刚好相反。Consumer接收一个参数,没有返回值
5.3 Runnable无参无返回型函数
Runnable的表现形式为即没有参数也没有返回值
5.4 Function函数
表现形式为接收一个参数,并返回一个值。Supplier、Consumer和Runnable可以看作Function的一种特殊表现形式
六、使用举例
在开发过程中经常会使用if…else…进行判断抛出异常、分支处理等操作。这些if…else…充斥在代码中严重影响了代码代码的美观,这时我们可以利用Java 8的Function接口来消灭if…else…。
if (...){
throw new RuntimeException("出现异常了");
}
if (...){
doSomething();
} else {
doOther();
}
使用小技巧:处理抛出异常的if
1.定义函数定义一个抛出异常的形式的函数式接口, 这个接口只有参数没有返回值是个消费型接口
/**
* 抛异常接口
**/
@FunctionalInterface
public interface ThrowExceptionFunction {
/**
* 抛出异常信息
*
* @param message 异常信息
* @return void
**/
void throwMessage(String message);
}
2.编写判断方法创建工具类VUtils并创建一个isTure方法,方法的返回值为刚才定义的函数式接口-ThrowExceptionFunction。ThrowExceptionFunction的接口实现逻辑为当参数b为true时抛出异常
/**
* 如果参数为true抛出异常
*
* @param b
* @return com.example.demo.func.ThrowExceptionFunction
**/
public static ThrowExceptionFunction isTure(boolean b){
return (errorMessage) -> {
if (b){
throw new RuntimeException(errorMessage);
}
};
}
3.使用方式调用工具类参数参数后,调用函数式接口的throwMessage方法传入异常信息。当出入的参数为false时正常执行
当出入的参数为true时抛出异常
4.更多使用举例见