6、Java中Lambda表达式使用及详解

一、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 表达式或者方法引用。函数式接口的主要用途是支持函数式编程,使得代码更加简洁和易于阅读。

特点:

  1. 单一抽象方法:函数式接口只能有一个未实现的方法,这使得它可以表示单一的行为。
  2. 默认方法:虽然只能有一个抽象方法,但函数式接口可以包含多个默认方法和静态方法
  3. @FunctionalInterface 注解:使用 @FunctionalInterface 注解可以清晰地表明一个接口是函数式接口,编译器会强制检查是否满足函数式接口的条件,就像@override注解一样,但不是强制的。
  4. 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.更多使用举例见

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值