lambda表达式学习使用实例

43 篇文章 1 订阅

前言

在对 Java8 发布的 Lambda 表达式进行一定了解之后,发现 Lambda 最核心的就是结合 Stream API 对集合数据的遍历、提取、过滤、排序等一系列操作的简化,以一种函数式编程的方式,对集合进行操作

lambda 表达式就是函数接口类型的实例

lambda 语法

基本语法:

(parameters) -> expression  或  (parameters) ->{ statements; }

基本语法的具体示例:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

lambda 语法使用

lambda 表达式实现 Runnable

lambda 表达式替换了原来匿名内部类的写法,没有了匿名内部类繁杂的代码实现,而是突出了,真正的处理代码。最好的示例就是实现 Runnable 的线程实现方式了: 用 () -> {}代码块替代了整个匿名类

public class LambdaTest1 {

    public static void main(String[] args) {

        // 使用匿名内部类 实现多线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类 实现多线程");
            }
        }).start();

        // 使用的lambda表达式语法为:() -> 5
        new Thread(() -> System.out.println("使用lambda表达式")).start();
    }
}

结果

匿名内部类 实现多线程
使用lambda表达式

lambda 表达式实现 forEach 遍历集合

实体类

public class Person {

    private String name;

    private int age;

    private String sex;

    public Person(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    
	// set,get 方法省略
}

使用语法: x -> 2 * x 进行 forEach 遍历

public class LambdaTest3 {

    @NotNull
    public static List<Person> getPersonList() {
        Person p1 = new Person("liu", 22, "male");
        Person p2 = new Person("zhao", 21, "male");
        Person p3 = new Person("li", 18, "female");
        Person p4 = new Person("wang", 21, "female");
        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        return list;
    }

    public static void main(String[] args) {

        List<Person> personList = LambdaTest3.getPersonList();
        // 使用 lambda 表达式 forEach 遍历
        personList.forEach(person -> System.out.println(person.toString()));
    }
}

结果:
Person{name='liu', age=22, sex='male'}
Person{name='zhao', age=21, sex='male'}
Person{name='li', age=18, sex='female'}
Person{name='wang', age=21, sex='female'}

forEach 遍历的语法解析

personList.forEach(person -> System.out.println(person.toString()));

查看 forEach 的源码,我们发现接收的参数类型为:Consumer<? super T> action

default void forEach(Consumer<? super T> action) {
	Objects.requireNonNull(action);
	for (T t : this) {
		action.accept(t);
	}
}

因此可推断 lambda 表达式的类型:

Consumer<Person> action = person -> System.out.println(person.toString());

如何理解这句代码呢?先看 java.util.function.Consumer 的源码:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    
    ...(省略default方法)
}

我们发现它是一个函数接口,通俗的话讲:函数接口是指只含有一个抽象方法的接口,在 Java8 中,接口里面可以存在已实现的方法,如:default 默认方法、static 静态方法

注意:@FunctionalInterface 注解的作用,当函数接口中存在两个或两个以上的抽象方法时,编译会报错

所以,我们可以这样理解:person -> System.out.println(person.toString()) 就是实现了 Consumer 接口中 accept 方法的实例,->前面的部分 e 表示 accept 方法的入参,->后面的部分表示 accept 方法的具体实现

注意:

  • 如果方法的实现部分由多行代码组成,就用 {} 括起来
  • 如果方法需要返回值,而且方法体只有一行代码,可以省略 return。如果方法体有多行代码,则不能省略 return

使用双冒号 :: 进行 forEach 遍历

public class LambdaTest3 {

    @NotNull
    public static List<Person> getPersonList() {
        Person p1 = new Person("liu", 22, "male");
        Person p2 = new Person("zhao", 21, "male");
        Person p3 = new Person("li", 18, "female");
        Person p4 = new Person("wang", 21, "female");
        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        return list;
    }

    public static void main(String[] args) {

        List<Person> personList = LambdaTest3.getPersonList();
        // 使用 lambda 表达式 forEach 遍历
        Consumer<Person> consumer = e -> e.setAge(e.getAge() + 3);
        personList.forEach(consumer);
        personList.forEach(System.out::println);
    }
}

结果:
Person{name='liu', age=25, sex='male'}
Person{name='zhao', age=24, sex='male'}
Person{name='li', age=21, sex='female'}
Person{name='wang', age=24, sex='female'}

双冒号::(方法引用)的语法解析

personList.forEach(System.out::println);

之前说过,lambda 表达式就是函数接口类型的实例,而创建该实例的过程,需要实现函数接口中的抽象方法。因此,可以采用引用的方式,即引用已定义好的方法去实现函数接口中的方法

那么 System.out::println 该如何理解?同样根据上下文推断 lambda 表达式的类型:

Consumer action = System.out::println;

System.out::println 的函数接口类型为 java.util.function.Consumer,即在产生该实例对象时,引用了 System.out 对象中的 println 方法,去实现了 Consumer 接口中的 accept 抽象方法。简而言之,即:引用 println 方法去实现了接口中的抽象方法

方法引用有很多种,下面主要讲述以下几种:

  • 构造方法引用:Class::new
  • 静态方法引用:ClassName::methodName
  • 实例上的实例方法引用:instanceReference::methodName
  • 类型上的实例方法引用:ClassName::methodName

方法引用详情查看

文章未完成,后续更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值