Lambda表达式的优雅

Lambda表达式的优雅

1.函数式编程思想

在这里插入图片描述

面向对象的思想:必须通过对象的形式来做事情,过分重视过程。

函数式编程思想:强调做什么,而不重视以什么方式去做。

2.多线程的传统写法

定义Runnable接口,并使用Thread类来启动线程。

// 匿名内部类
Runnable task = new Runnable() {
    @Override
    public void run() { // 覆盖重写抽象方法
        System.out.println("多线程任务执行!");
    }
};
new Thread(task).start(); // 启动线程

缺点:

  • Thread类过度依赖Runnable接口作为参数
  • 必须覆盖run方法

而事实上,只有run方法才是关键所在。

3.转换编程思维

如果我们将关注点从“怎么做”转移到”做什么“的本质上,就会发现只要达到自己的目的,过程与形式并不重要。

比如:当我们需要从北京到上海时,可以选择高铁、汽车、骑行或是徒步。我们的真正目的是到达上海,而如何才能到达上海的形式并不重要,所以我们一直在探索有没有比高铁更好的方式—搭乘飞机。

在这里插入图片描述

而现在这种飞机(甚至是飞船)已经诞生:2014年3月Oracle所发布的Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。

在这里插入图片描述

4.Lambda的用法

Lambda省去面向对象的条条框框,格式由3个部分组成:

  • 参数:语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • 箭头:新引入的语法格式,代表指向动作。
  • 业务代码:与传统业务代码语法一致。

Lambda表达式的标准格式为:

(参数类型 参数名) -> { 业务代码 }

借助Java 8的全新语法,上述Runnable接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效:

new Thread(() -> System.out.println("多线程任务执行!")).start(); // 启动线程

不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!

Lambda无参无返回

//函数式接口
public interface Cook {
    void makeFood();
}
public static void main(String[] args) {
    invokeCook(() -> {
        System.out.println("吃饭啦!");
    });
}
//吃饭啦!

备注:小括号代表Cook接口makeFood抽象方法的参数为空,大括号代表makeFood的方法体。

Lambda有参有返回

需求:Person对象按年龄升序排序

@Data
@AllArgsConstructor
public class Person { 
    private String name;//姓名
    private int age;//年龄
}

传统写法与Lambda写法的比较:

Person[] array = {
    new Person("张三", 19),
    new Person("李四", 18),
    new Person("王五", 20) };

/******传统写法******/
// 匿名内部类
Comparator<Person> comp = new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getAge() - o2.getAge();
    }
};
// 第二个参数为排序规则,即Comparator接口实例
Arrays.sort(array, comp); 

/******Lambda写法******/
Arrays.sort(array, (Person a, Person b) -> {
    return a.getAge() - b.getAge();
});

5.Lambda省略格式

省略条件:

在Lambda标准格式的基础上,使用省略写法的规则为:

  • 小括号内参数的类型可以省略;
  • 如果小括号内有且仅有一个参数,则小括号可以省略;
  • 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

仍然使用前文含有唯一makeFood抽象方法的厨子Cook接口,使用Lambda省略格式写法:

private static void invokeCook(Cook cook) {
	cook.makeFood();
	}
	
public static void main(String[] args) {
    //省略参数类型及参数、大括号、return关键字、语句分号
  	invokeCook(() -> System.out.println("吃饭啦!"));
  	}

6.Lambda的延迟执行

性能浪费的日志案例:

private static void log(int level, String msg) {
    if (level == 1) {
        log.info(msg);
    }
}

public static void main(String[] args) {
    String msgA = "Hello";
	String msgB = "World";
	log(1, msgA + msgB );
    //log(2, msgA + msgB );
}

问题:无论级别是否满足要求,作为 log 方法的第二个参数,三个字符串一定会首先被拼接并传入方法内然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。

体验Lambda优雅写法

public interface MessageBuilder {
    String buildMessage();
}
private static void log(int level, MessageBuilder builder) {
    if (level == 1) {
        log.info(builder.buildMessage());
    }
}

public static void main(String[] args) {
    String msgA = "Hello";
    String msgB = "World";
    log(2, ()> {
        //Lambda延迟执行,如果level日志级别不满足,则Lambda不会执行
        System.out.println("Lambda执行!");
        return msgA + msgB;
    });
}

从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。

7.方法引用

双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。

推导与省略

如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式—它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。

通过对象名引用成员方法

public class MethodRefObject {
    public void printUpperCase(String str) {
        System.out.println(str.toUpperCase());
    }
}
public interface Printable {
    void print(String str);
}
private static void printString(Printable lambda) {
    lambda.print("Hello");
}
public static void main(String[] args) {
    MethodRefObject obj = new MethodRefObject();
    //输出Hello
    printString(obj::printUpperCase);
}

通过类名引用静态方法

public interface Calculable {
    int calc(int num);
}
private static void method(int num, Calcable lambda) {
    System.out.println(lambda.calc(num));
}
public static void main(String[] args) {
    //使用双冒号调用Math类静态方法abs()
    method(10, Math::abs);
}

通过this、super引用子类及父类成员方法

public interface Greeted {
    void greet();
}
public class Father {
    public void sayHello(){
        System.out.println("大家好,我是Father");
    }
}
public class Son extends Father {
    @Override
    public void sayHello() {
        System.out.println("大家好,我是Son");
    }

    public void method(Greeted g){
        g.greet();
    }

    public void show(){
        //调用子类sayHello()方法
        method(this::sayHello);
        //调用父类sayHello()方法
        method(super::sayHello);
    }
}

构造方法的引用

@Data
@AllArgsConstructor
public class Person {
    private String name;
}
public interface PersonBuilder {
    Person buildPerson(String name);
}
public static void printName(String name, PersonBuilder builder) {
    System.out.println(builder.buildPerson(name));
}

public static void main(String[] args) {
    //使用双冒号调用有参构造方法
    printName("小明", Person::new);
}

数组构造的引用

public interface ArrayBuilder {
    int[] buildArray(int length);
}
private static int[] initArray(int length, ArrayBuilder builder) {
    return builder.buildArray(length);
}
public static void main(String[] args) {
    //使用双冒号构造数组
    int[] array = initArray(10, int[]::new);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第二范式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值