Lambda表达式简单介绍

Lambda表达式简单介绍

一、Lambda表达式简介

什么是Lambda?

Lambda是JAVA 8添加的新特性,说白了,Lambda是一个匿名函数

为什么使用Lambda

使用Lambda表达式可以对一个接口的方法进行非常简洁的实现

对比以往的借口实现方式,可以发现Lambda表达式的简洁之处:

package cn.dbt;

public interface Comparator {
    int compare(int a,int b);
}

package cn.dbt;

public class ComparatorImpl implements Comparator {
    @Override
    public int compare(int a, int b) {
        return a - b;
    }
}

package cn.dbt;

public class Program {
    public static void main(String[] args) {

        //1.使用接口实现类的
        ComparatorImpl comparator = new ComparatorImpl();

        //2.使用匿名内部类
        Comparator comparator1 = new Comparator() {
            @Override
            public int compare(int a, int b) {
                return a - b;
            }
        };

        //3.使用Lambda表达式
        Comparator comparator2 = (a, b) -> a - b;
    }
}

Lambda对接口的要求

虽然可以使用Lambda表达式对某些接口进行简单的实现,但是并不是所有的接口都可以用Lambda表达式来实现,要求接口中定义的必须要实现的抽象方法只能是一个

在JAVA8中 ,对接口加了一个新特性:default
可以使用default对接口方法进行修饰,被修饰的方法在接口中可以默认实现

@FunctionalInterface

修饰函数式接口的,要求接口中的抽象方法只有一个,否则会报错

package cn.dbt;
@FunctionalInterface
public interface Comparator {
    int compare(int a,int b);
}

二、Lambda的基础语法

1.语法

// 1.Lambda表达式的基础语法
// Lambda是一个匿名函数 一般关注的是以下两个重点
// 参数列表 方法体

/**
* ():用来描述参数列表
*  {}:用来描述方法体 有时可以省略
*  ->: Lambda运算符 读作goes to
*  例 Test t=()->{System.out.println("hello word")}; 大括号可省略
*/

2.创建多个接口

/**
 * 无参数无返回值接口
 * @author dbt
 * @version 1.0
 * @date 2022/4/23 11:16
 */
@FunctionalInterface
public interface LambdaNoneReturnNoneParmeter {

    void test();
}

/**
 * 无返回值有单个参数
 * @author dbt
 * @version 1.0
 * @date 2022/4/23 11:16
 */
@FunctionalInterface
public interface LambdaNoneReturnSingleParmeter {

    void test(int n);
}

/**
 * 无返回值 多个参数的接口
 * @author dbt
 * @version 1.0
 * @date 2022/4/23 11:16
 */
@FunctionalInterface
public interface LambdaNoneReturnMutipleParmeter {

    void test(int a,int b);
}

/**
 * 有返回值 无参数接口
 * @author dbt
 * @version 1.0
 * @date 2022/4/23 11:16
 */
@FunctionalInterface
public interface LambdaSingleReturnNoneParmeter {

    int test();
}

/**
 * 有返回值 有单个参数的接口
 * @author dbt
 * @version 1.0
 * @date 2022/4/23 11:16
 */
@FunctionalInterface
public interface LambdaSingleReturnSingleParmeter {

    int test(int n);
}

/**
 * 有返回值 有多个参数的接口
 * @author dbt
 * @version 1.0
 * @date 2022/4/23 11:16
 */
@FunctionalInterface
public interface LambdaSingleReturnMutipleParmeter {

    int test(int a,int b);
}

3.创建测试类

package cn.dbt.syntax;

import cn.dbt.interfaces.*;

/**
 * @author dbt
 * @date 2022/4/23 11:16
 */
public class Syntax1 {
    public static void main(String[] args) {
        LambdaNoneReturnNoneParameter lambda1 = () -> {
            System.out.println("lambda1");
        };

        lambda1.test();

        LambdaNoneReturnSingleParameter lambda2 = (int a) -> {
            System.out.println(a);
        };

        lambda2.test(10);

        LambdaNoneReturnMultipleParameter lambda3 = (int a, int b) -> {
            System.out.println(a + b);
        };

        lambda3.test(20, 30);

        LambdaSingleReturnNoneParameter lambda4 = () -> {
            return 40;
        };

        int test = lambda4.test();
        System.out.println(test);

        LambdaSingleReturnSingleParameter lambda5 = (int a) -> {
            return a;
        };

        int test1 = lambda5.test(50);
        System.out.println(test1);

        LambdaSingleReturnMultipleParameter lambda6 = (int a, int b) -> {
            return a + b;
        };

        int test2 = lambda6.test(60, 70);
        System.out.println(test2);
    }
}

三、语法精简

针对上述基础语法的精简

1.参数类型精简

/**
* 语法精简
* 1.参数类型
* 由于在接口的抽象方法中,已经定义了参数的数量类型 所以在Lambda表达式中参数的类型可以省略
* 备注:如果需要省略类型,则每一个参数的类型都要省略,千万不要一个省略一个不省略
*/
LambdaNoneReturnMultipleParameter lambda1=(int a,int b)-> {
    System.out.println("hello world"); 
};    
可以精简为:
LambdaNoneReturnMutlipleParameter lambda1=(a,b)-> {
    System.out.println("hello world");
};

2.参数小括号精简

/**
* 2.参数小括号
* 如果参数列表中,参数的数量只有一个 此时小括号可以省略
*/
LambdaNoneReturnSingleParameter lambda2=(a)->{
    System.out.println("hello world");
};
可以精简为:
LambdaNoneReturnSingleParameter lambda2= a->{
    System.out.println("hello world");
};

3.方法大括号精简

/**
* 3.方法大括号
* 如果方法体中只有一条语句,此时大括号可以省略
*/
LambdaNoneReturnSingleParameter lambda3=a->{
    System.out.println("hello world");
};
可以精简为:
LambdaNoneReturnSingleParmeter lambda3=a->System.out.println("hello world");

4.大括号精简补充

/**
* 4.如果方法体中唯一的一条语句是一个返回语句
* 贼省略大括号的同时 也必须省略return
*/
LambdaSingleReturnNoneParameter lambda4=()->{
    return 10;
};
可以精简为:
LambdaSingleReturnNoneParmeter lambda4=()->10;

四、Lambda语法进阶

1.方法引用(普通方法与静态方法)

在实际使用过程中,一个接口很可能有多个相同实现,此时如果一个一个的去写lambda表达式,会很浪费时间,并且维护难度很大,此时我们可以采用另外一种做法,方法引用

/**
*方法引用:
* 可以快速的将一个Lambda表达式的实现指向一个已经实现的方法
* 方法的隶属者 如果是静态方法,隶属的就是一个类,其他的话用所在类对象引用,详见举例
* 语法:方法的隶属者::方法名
* 注意:
*  1.引用的方法中,参数数量和类型一定要和接口中定义的方法一致
*  2.返回值的类型也一定要和接口中的方法一致
*/

代码如下:

package cn.dbt.syntax;

import cn.dbt.interfaces.LambdaSingleReturnSingleParameter;
import com.sun.xml.internal.ws.client.sei.ResponseBuilder;

/**
 * @Author dbt
 * @Time 2022/4/23 17:34
 */
public class Syntax3 {
    public static void main(String[] args) {
        LambdaSingleReturnSingleParameter lambda1 = a -> a * 2;
        LambdaSingleReturnSingleParameter lambda2 = a -> a * 2;
        LambdaSingleReturnSingleParameter lambda3 = a -> a * 2;

        //此时代码重复度过高,可以简化如下:
        LambdaSingleReturnSingleParameter lambda4 = a -> change(a);
        int test = lambda4.test(10);
        System.out.println(test);

        //用::实现
        LambdaSingleReturnSingleParameter lambda5 = Syntax3::change;
        LambdaSingleReturnSingleParameter lambda6= new Syntax3()::change2;
        int test1 = lambda5.test(20);
        int test2 = lambda6.test(30);
        System.out.println(test1);
        System.out.println(test2);

    }

    private static int change(int a) {
        return a * 2;
    }

    //非静态
    public int change2(int a) {
        return a * 4;
    }

}

运行结果:

请添加图片描述

2.构造方法引用

先创建一个实体类

package cn.dbt.data;

/**
 * @Author dbt
 * @Time 2022/4/23 18:08
 */
public class Person {
    public String name;
    public int age;

    public Person() {
        System.out.println("这是Person的无参构造");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("这是Person的全参构造");
    }
}

设置两个接口,用来调用此类的无参和全参构造

package cn.dbt.interfaces;

import cn.dbt.data.Person;

/**
 * @Author dbt
 * @Time 2022/4/23 18:10
 */
public interface PersonCreator {
    Person getPerson();
}

package cn.dbt.interfaces;

import cn.dbt.data.Person;

/**
 * @Author dbt
 * @Time 2022/4/23 18:14
 */
public interface PersonCreator2 {
    Person getPerson(String name, int age);
}

测试

package cn.dbt.syntax;

import cn.dbt.data.Person;
import cn.dbt.interfaces.PersonCreator;
import cn.dbt.interfaces.PersonCreator2;

/**
 * @Author dbt
 * @Time 2022/4/23 18:02
 */
public class Syntax4 {
    public static void main(String[] args) {
        PersonCreator creator = () -> new Person();

        //构造方法的引用
        //仅是一种简写方式
        PersonCreator creator1 = Person::new;
        PersonCreator2 creator2 = Person::new;

        creator1.getPerson();
        creator2.getPerson("Jack", 20);

        creator.getPerson();
    }
}

测试结果:

请添加图片描述

同样可以看出,lambda表达式只是一种简写方式,内部创建对象调用方法的逻辑与传统无异。

五、综合练习

1.排序问题,先贴结果,再补过程

package cn.dbt.exercise;

import cn.dbt.data.Person;

import java.util.ArrayList;
import java.util.Comparator;

/**
 * @Author dbt
 * @Time 2022/4/23 19:23
 */
public class Exercise1 {
    //集合排序
    public static void main(String[] args) {
        //需求:已知在一个ArrayList中有若干个Person对象,将这些对象按照年龄顺序进行降序排序
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("Jack", 10));
        list.add(new Person("Rose", 12));
        list.add(new Person("Tom", 13));
        list.add(new Person("Jerry", 14));
        list.add(new Person("李雷", 11));
        list.add(new Person("韩梅梅", 8));
        list.add(new Person("Jack", 10));

        /*Comparator判断不看具体o1,o2的数值,只看正负:
         * 当返回值>0,o1>o2
         * 返回值<0,o1<o2
         * 返回值=0,,o1=o2*/

//        Comparator<Person> comparator = (o1, o2) -> {
//            return o2.age - o1.age;
//        };
//        list.sort(comparator);

        list.sort((o1, o2) -> o2.age - o1.age);
        System.out.println(list);
    }
}

不要忘记重写Person的toString()方法,运行结果如下:

请添加图片描述

sort()为list中排序的方法,点开源码如下:

请添加图片描述

但其中Comparator我们也不知道是什么东西,继续点开

请添加图片描述

可以看到,是一个函数式接口,继续下拉:

请添加图片描述

看到了其中的比较方法,实现此方法即可

也就是说完整的排序写法应该如下:

        /*Comparator判断不看具体o1,o2的数值,只看正负:
         * 当返回值>0,o1>o2
         * 返回值<0,o1<o2
         * 返回值=0,,o1=o2*/
        Comparator<Person> comparator = (o1, o2) -> {
            return o2.age - o1.age;
        };
        list.sort(comparator);

//        list.sort((o1, o2) -> o2.age - o1.age);
        System.out.println(list);

只不过简化了而已

2.TreeSet中实现Comparator接口

TreeSet有两个构造方法,可以使用第二个

请添加图片描述

具体过程和刚刚类似

package cn.dbt.exercise;

import cn.dbt.data.Person;

import java.util.TreeSet;

/**
 * @Author dbt
 * @Time 2022/4/24 21:29
 */
public class Exercise2 {
    public static void main(String[] args) {
        TreeSet<Person> set = new TreeSet<>((o1,o2)->{
            return  (o1.age-o2.age>=0)? -1:1;
        });

        set.add(new Person("Jack", 10));
        set.add(new Person("Rose", 12));
        set.add(new Person("Tom", 13));
        set.add(new Person("Jerry", 14));
        set.add(new Person("李雷", 11));
        set.add(new Person("韩梅梅", 8));
        set.add(new Person("Jack", 10));

        System.out.println(set);
    }
}

运行结果如下:

请添加图片描述

3.用forEach()方法对集合进行遍历

package cn.dbt.exercise;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
 * @Author dbt
 * @Time 2022/4/24 22:24
 */
public class Exercise3 {
    public static void main(String[] args) {
        //集合的遍历
        ArrayList<Integer> list = new ArrayList<>();

        Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9);

        //将集合中的每一个元素都带入到方法accept中
//        list.forEach(System.out::println);

        list.forEach(ele -> {
            if (ele % 2 == 0) {
                System.out.println(ele);
            }
        });
    }
}

结果如下:

请添加图片描述
请添加图片描述

4.删除集合中满足条件的元素

此原理与上条类似

package cn.dbt.exercise;

import cn.dbt.data.Person;

import java.util.ArrayList;

/**
 * @Author dbt
 * @Time 2022/4/26 21:59
 */
public class Exercise4 {
    public static void main(String[] args) {
        //需求,删除集合中满足条件的元素
        ArrayList<Person> list = new ArrayList<>();

        list.add(new Person("Jack", 19));
        list.add(new Person("Rose", 18));
        list.add(new Person("Tom", 13));
        list.add(new Person("Jerry", 14));
        list.add(new Person("李雷", 11));
        list.add(new Person("韩梅梅", 8));
        list.add(new Person("Jack", 10));

        //lambda进行实现
        //将集合中的每一个元素都带入到test方法中,如果返回值是true,则删除元素
        list.removeIf(ele->ele.age>15);
        System.out.println(list);
    }
}

查看removeIf()源码:

请添加图片描述
请添加图片描述

实现Predicate借口即可

5.利用lambda表达式创建一个新线程

同样,我们也可以使用lambda表达式创建一个新的线程,打开Thread源码

请添加图片描述

点开Runnanle借口

请添加图片描述

可以发现同样是函数式接口,用lambda表达式实现即可

package cn.dbt.exercise;

/**
 * @Author dbt
 * @Time 2022/4/26 22:13
 */
public class Exercise5 {
    public static void main(String[] args) {
        //需求:开辟一个线程,做一个数字的输出
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
        });

        thread.start();
    }
}

运行结果:

请添加图片描述

六、系统内置的函数式接口

package cn.dbt.functional;

/**
 * @Author dbt
 * @Time 2022/4/26 22:25
 */
public class FunctionalInterface {
    public static void main(String[] args) {

        // Predicate<T>              :     参数是T 返回值boolean
        // 在后续如果一个接口需要指定类型的参数,返回boolean时可以指向 Predicate
        //          IntPredicate            int -> boolean
        //          LongPredicate           long -> boolean
        //          DoublePredicate         double -> boolean

        // Consumer<T>               :      参数是T 无返回值(void)
        //          IntConsumer             int ->void
        //          LongConsumer            long ->void
        //          DoubleConsumer          double ->void

        // Function<T,R>             :      参数类型T  返回值R
        //          IntFunction<R>          int -> R
        //          LongFunction<R>         long -> R
        //          DoubleFunction<R>       double -> R
        //          IntToLongFunction       int -> long
        //          IntToDoubleFunction     int -> double
        //          LongToIntFunction       long -> int
        //          LongToDoubleFunction    long -> double
        //          DoubleToLongFunction    double -> long
        //          DoubleToIntFunction     double -> int

        // Supplier<T> : 参数 无 返回值T
        // UnaryOperator<T> :参数T 返回值 T
        // BiFunction<T,U,R> : 参数 T、U 返回值 R
        // BinaryOperator<T> :参数 T、T 返回值 T
        // BiPredicate<T,U> :  参数T、U  返回值 boolean
        // BiConsumer<T,U> :    参数T、U 无返回值

        /**
         * 常用的 函数式接口
         * Predicate<T>、Consumer<T>、Function<T,R>、Supplier<T>
         */
    }
}

七、Lambda闭包

先上代码,再解释:

package cn.dbt.closure;

import java.util.function.Supplier;

/**
 * @Author dbt
 * @Time 2022/4/27 22:15
 */
public class ClosureDemo {
    public static void main(String[] args) {

        int n = getNumber().get();

        System.out.println(n);

    }

    private static Supplier<Integer> getNumber() {
        int num = 10;
        return () -> {
            return num;
        };
    }
}

结果:

请添加图片描述

众所周知,一个局部变量,在方法结束之后就应该销毁,但此时在方法结束后,num仍然给n赋值了,并没有消失。

这是因为闭包会提升变量的生命周期,使我们可以获取某些方法中的局部变量

第二个例子:

package cn.dbt.closure;

import java.util.function.Consumer;

/**
 * @Author dbt
 * @Time 2022/4/27 22:22
 */
public class ClosureDemo2 {
    public static void main(String[] args) {
        int a = 10;
        Consumer<Integer> consumer = ele -> {
            System.out.println(a);
//            System.out.println(a+1);
//            System.out.println(a++);
        };

        consumer.accept(1);
        consumer.accept(a);
    }
}

结果:

请添加图片描述

当然,输出a+1也可获得11的结果,不再赘述,但使用a++时会报错:

请添加图片描述

这是因为lambda表达式中引用的变量需要是常量,在编译时会自动增加final,见下:

请添加图片描述

同样不行,所以不要对lambda表达式引用的变量进行修改

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值