函数式接口与Lambda表达式

1. Lambda表达式的简介

1.1. Lambda表达式的概念

Lambda表达式,是java8的一个新特性,也是java8中最值得学习的新特性之一。
Lambda表达式,从本质来讲,是一个匿名函数。可以使用这个匿名函数,实现接口中的方法,对接口进行非常简洁的实现,从而简化代码。

1.2. Lambda表达式的使用场景

通常来讲,使用Lambda表达式。是为了简化接口实现的。
关于接口实现,可以有多多种方式,例如:设计接口的实现类,使用匿名内部类,但是Lambda表达式,比这两种方式都简单。

1.3. Lambda表达式对接口的要求

虽然说,Lambda表达式可以在一定程度上简化接口的实现,但是,并不是所有的接口都可以使用Lambda表达式来简洁实现。Lambda表达式毕竟只是一个匿名函数方法。当实现的接口中的方法过多或者过少的时候,Lambda表达式都是不适用的。
Lambda表达式只能实现函数式接口

1.4. 函数式接口

1.4.1. 基础概念

如果说,一个接口中,要求实现类必须实现的抽象方法有且只有一个!这样的接口,就是函数式接口。
实例:

//这个接口中,有且只有一个方法,是实现类必须实现的,因此是一个函数式接口
interface Test1 {
    void test();
}
//这个接口中,实现类必须实现的方法有两个,因此不是函数式接口
interface Test2{
    void test1();
    void test2();
}
//这个接口中,实现类必须要实现的方法有零个,因此不是一个函数式接口
interface Test3{

}
//这个接口中,虽然没有定义任何的方法,但是可以从父接口中继承到一个抽象方法,是一个函数式接口
interface Test4 extends Test1{

}
//这个接口,虽然里面定义了两个方法,但是default方法子类不是必须实现的。
//因此,实现类实现这个接口的时候,必须实现的方法只有一个,是一个函数式接口
interface Test5{
    void test5();
    default void test(){}
}
//这个接口中的toString方法,是Object类中定义的方法。
//此时,实现类在实现接口时,toString方法可以不重写,因为可以从父类Object中继承的
//此时,实现类,在实现接口的时候,有且只有一个方法必须实现重写,是一个函数接口
interface  Test6{
    void test6();
    String toString();
}
//不是一个函数式接口
interface Test7{

}
//是一个函数接口
interface test8{
    void test();
    default void test1(){};
    static  void test2(){};
    String toString();
}

1.4.2. @FunctionalInterface

是一个注解,用在接口之前,判断这个接口是不是一个函数式接口,如果是函数式接口,没有任何问题,如果不是函数式接口,则会报错,功能类似于@Override
实例:

@FunctionalInterface
interface test8{
    void test();
    default void test1(){};
    static  void test2(){};
    String toString();
}

2. Lambda表达式语法

准备几个函数式接口

/**
 * @author qwy
 * @create 2021-03-31 16:24
 **/
//无返回值,无参的函数接口
@FunctionalInterface
interface Test1{

    void test();
}
//无返回值,一个参数的函数接口
@FunctionalInterface
interface Test2{
    void test(int age);
}
//无返回值,多个参数的函数接口
interface Test3{
    void test(int age,String name);
}
//有返回值,无参函数接口
interface  Test4{
    int  test();
}
//有返回值,一个参数的函数接口
interface Test5{
    String test(String name);
}
//有返回值,多个参数的函数接口
interface Test6{
    String test(String name,int age);
}

2.2. Lambda表示的基础语法

Lambda表达式,其实本质就是一个匿名函数,因此在写Lambda表达式的时候,不需要关心方法名是什么。实际上,我们在写Lambda表达式的时候,也不需要关心返回类型。
只需要关心两部分内容即可:参数列表方法体
Lambda表达式的基础语法:
(参数)->{
方法体;
}
参数部分:方法的参数列表,要求和实现的接口中的方法参数部分一致,包括参数的数量和类型。
方法体部分:方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回
->:分割参数部分和方法体部分。
实例:

public class TestFunctionalInterface {
    public static void main(String[] args) {
        //实现无返回值,无参数的函数式接口
        Test1 t1=()->{
            System.out.println("无返回,无参数的函数");
        };
        t1.test();
        System.out.println("*******************");
        //实现无返回值,一个参数的函数式接口
        Test2 t2=(int age)->{
            System.out.println("无返回,一个参数的函数");
            System.out.println("age = " + age);
        };
        t2.test(12);
        System.out.println("*******************");
        //实现无返回值,多个参数的函数式接口
        Test3 t3=(int age,String name)->{
            System.out.println("无返回值,多个参数的函数");
            System.out.println("age = " + age);
            System.out.println("name = " + name);
        };
        t3.test(12,"张三");
        System.out.println("*******************");
        //实现有返回值,无参数的函数式接口
        Test4 t4=()->{
            System.out.println("有返回值,无参数函数");
            return 12;
        };
        int age = t4.test();
        System.out.println("age = " + age);
        System.out.println("*******************");
        //实现有返回值,一个参数的函数式接口
        Test5 t5=(String name)->{
            System.out.println("有返回值,一个参数的函数");
            return name;
        };
        String name = t5.test("李四");
        System.out.println("name = " + name);
        System.out.println("*******************");
        //实现有返回值,多个参数的函数式接口
        Test6 t6= (String n, int a)->{
            System.out.println("有返回值,多个参数的函数");
            return "name->"+name+",age->"+age;
        };
        String str = t6.test("王五", 13);
        System.out.println("str = " + str);
    }

}

运行结果:
无返回,无参数的函数
#*******************
无返回,一个参数的函数
age = 12
#*******************
无返回值,多个参数的函数
age = 12
name = 张三
#*******************
有返回值,无参数函数
age = 12
#*******************
有返回值,一个参数的函数
name = 李四
#*******************
有返回值,多个参数的函数
str = name->李四,age->12

2.2. lambda表达式语法进阶

在上面例子中,的确可以使用Lambda表达式实现接口,但是依然不够简洁,有简化的空间。

2.2.1. 参数部分的精简

参数的类型
由于在接口的方法中,已经定义了每一个参数的类型是什么,而且在使用Lambda表达式实现接口的时候,必须要保证参数的数量和类型需要和接口中的方法保持一致,因此,Lambda表达式中参数类型可以省略不写。
注意:如果需要省略参数的类型,要保证:每一个参数的类型都必须省略不写,绝对不能出现有的参数类型省略了,有的参数类型没有省略。
实例:

public class TestFunctionalInterface {
    public static void main(String[] args) {
        //实现无返回值,无参数的函数式接口
        Test1 t1=()->{
            System.out.println("无返回,无参数的函数");
        };
        t1.test();
        System.out.println("*******************");
        //实现无返回值,一个参数的函数式接口
        Test2 t2=(age)->{
            System.out.println("无返回,一个参数的函数");
            System.out.println("age = " + age);
        };
        t2.test(12);
        System.out.println("*******************");
        //实现无返回值,多个参数的函数式接口
        Test3 t3=(age,name)->{
            System.out.println("无返回值,多个参数的函数");
            System.out.println("age = " + age);
            System.out.println("name = " + name);
        };
        t3.test(12,"张三");
        System.out.println("*******************");
        //实现有返回值,无参数的函数式接口
        Test4 t4=()->{
            System.out.println("有返回值,无参数函数");
            return 12;
        };
        int age = t4.test();
        System.out.println("age = " + age);
        System.out.println("*******************");
        //实现有返回值,一个参数的函数式接口
        Test5 t5=( name)->{
            System.out.println("有返回值,一个参数的函数");
            return name;
        };
        String name = t5.test("李四");
        System.out.println("name = " + name);
        System.out.println("*******************");
        //实现有返回值,多个参数的函数式接口
        Test6 t6= (n, a)->{
            System.out.println("有返回值,多个参数的函数");
            return "name->"+name+",age->"+age;
        };
        String str = t6.test("王五", 13);
        System.out.println("str = " + str);
    }

}

运行结果:与上例无异。
参数的小括号:
如果方法的参数列表中参数数量有且只有一个,此时,参数列表的小括号是可以省略不写的。
注意:
1.只有当参数数量是一个时候,多了,少了,都不能省略
2.省略小括号的同时,必须省略参数的类型

public class TestFunctionalInterface {
    public static void main(String[] args) {
        //不能省略小括号
        Test1 t1=()->{
            System.out.println("无返回,无参数的函数");
        };
        t1.test();
        System.out.println("*******************");
        //能省略小括号
        Test2 t2 = age -> {
            System.out.println("无返回,一个参数的函数");
            System.out.println("age = " + age);
        };
        t2.test(12);
        System.out.println("*******************");
        //不能省略小括号
        Test3 t3=(age,name)->{
            System.out.println("无返回值,多个参数的函数");
            System.out.println("age = " + age);
            System.out.println("name = " + name);
        };
        t3.test(12,"张三");
        System.out.println("*******************");
        //不能省略小括号
        Test4 t4=()->{
            System.out.println("有返回值,无参数函数");
            return 12;
        };
        int age = t4.test();
        System.out.println("age = " + age);
        System.out.println("*******************");
        //能省略小括号
        Test5 t5= name ->{
            System.out.println("有返回值,一个参数的函数");
            return name;
        };
        String name = t5.test("李四");
        System.out.println("name = " + name);
        System.out.println("*******************");
        //不能省略小括号
        Test6 t6= (n, a)->{
            System.out.println("有返回值,多个参数的函数");
            return "name->"+name+",age->"+age;
        };
        String str = t6.test("王五", 13);
        System.out.println("str = " + str);
    }

}

运行结果:与上例无异。

2.2.2. 方法体部分的精简

方法体大括号的精简
当一个方法体中的逻辑,有且只有一句的情况下,大括号可以省略。

public class TestFunctionalInterface {
    public static void main(String[] args) {
        //可以省略方法体
        Test1 t1=()-> System.out.println("无返回,无参数的函数");
        t1.test();
        System.out.println("*******************");
        //不可以省略方法体
        Test2 t2 = age -> {
            System.out.println("无返回,一个参数的函数");
            System.out.println("age = " + age);
        };
        t2.test(12);
        System.out.println("*******************");
        //不可以省略方法体
        Test3 t3=(age,name)->{
            System.out.println("无返回值,多个参数的函数");
            System.out.println("age = " + age);
            System.out.println("name = " + name);
        };
        t3.test(12,"张三");
        System.out.println("*******************");
        //不可以省略方法体
        Test4 t4=()->{
            System.out.println("有返回值,无参数函数");
            return 12;
        };
        int age = t4.test();
        System.out.println("age = " + age);
        System.out.println("*******************");
        //不可以省略方法体
        Test5 t5= name ->{
            System.out.println("有返回值,一个参数的函数");
            return name;
        };
        String name = t5.test("李四");
        System.out.println("name = " + name);
        System.out.println("*******************");
        //不可以省略方法体
        Test6 t6= (n, a)->{
            System.out.println("有返回值,多个参数的函数");
            return "name->"+name+",age->"+age;
        };
        String str = t6.test("王五", 13);
        System.out.println("str = " + str);
    }

}

运行结果:参照上例。
return 的精简
如果一个方法中唯一的一条语句是一个返回语句,此时省略大括号的同时,也必须省略return。
实例:

public class TestFunctionalInterface {
    public static void main(String[] args) {
        //可以省略方法体
        Test1 t1=()-> System.out.println("无返回,无参数的函数");
        t1.test();
        System.out.println("*******************");
        //不可以省略方法体
        Test2 t2 = age -> {
            System.out.println("无返回,一个参数的函数");
            System.out.println("age = " + age);
        };
        t2.test(12);
        System.out.println("*******************");
        //不可以省略方法体
        Test3 t3=(age,name)->{
            System.out.println("无返回值,多个参数的函数");
            System.out.println("age = " + age);
            System.out.println("name = " + name);
        };
        t3.test(12,"张三");
        System.out.println("*******************");
        //省略方法体
        Test4 t4=()-> 12;

        int age = t4.test();
        System.out.println("age = " + age);
        System.out.println("*******************");
        //省略方法体
        Test5 t5= name -> name;

        String name = t5.test("李四");
        System.out.println("name = " + name);
        System.out.println("*******************");
        //省略方法体
        Test6 t6= (n, a)-> "name->"+name+",age->"+age;
        String str = t6.test("王五", 13);
        System.out.println("str = " + str);
    }

}

运行结果:
无返回,无参数的函数
#*******************
无返回,一个参数的函数
age = 12
#*******************
无返回值,多个参数的函数
age = 12
name = 张三
#*******************
age = 12
#*******************
name = 李四
#*******************
str = name->李四,age->12

3.函数的引用

Lambda表达式是为了简化接口的实现的,在Lambda表达式中,不应该出现比较复杂的逻辑,如果在Lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。如果在Lambda表达式中需要处理的逻辑比较复杂。一般情况下会单独的写一个方法。在Lambda表达式中直接引用这个方法即可。
引用函数:引用一个已经存在的方法,使其替代Lambda表达式完成接口的实现。
实例:

package com.qwy;

/**
 * @author qwy
 * @create 2021-03-31 20:36
 **/
//定义函数式接口
interface Test1{
    int add(int a,int b);
}
//定义普通类,用来被接口函数调用
class MyTest{
    public int save(int a,int b){
        if(a>b){
            return a+b;
        }else  if(a<b){
            return  a-b;
        }else{
            return a;
        }
    }
}
public class TestLambda {
    public static void main(String[] args) {
        Test1 t1=(a,b)->new MyTest().save(a,b);
        int rs = t1.add(3, 4);
        System.out.println("rs = " + rs);
    }
}

运行结果:
rs = -1

3.1. 静态方法的引用

语法:
类::静态方法
注意:
1.在引用的方法后面,不要添加小括号
2.引用的这个方法,参数(数量,类型)和返回值,必须和接口中定义的方法一致。
实例:

package com.qwy;

/**
 * @author qwy
 * @create 2021-03-31 20:36
 **/
//定义函数式接口
interface Test1{
    int add(int a,int b);
}
//定义普通类,用来被接口函数调用
class MyTest{
    //将方法静态化
    public static int save(int a,int b){
        if(a>b){
            return a+b;
        }else  if(a<b){
            return  a-b;
        }else{
            return a;
        }
    }
}
public class TestLambda {
    public static void main(String[] args) {
        //引用静态方法:注意 //静态函数引用:注意不要写()参数列表
        Test1 t1=MyTest::save;
        int rs = t1.add(3, 4);
        System.out.println("rs = " + rs);
    }
}

运行结果:
rs = -1
注意://静态函数引用:注意不要写()参数列表

3.2. 非静态方法的引用

语法:
对象::非静态方法
注意:
在引用的方法后面,不要加小括号。
引用的这个方法,参数(个数,类型)和返回值,必须和函数接口定义的一致。
实例:

package com.qwy;

/**
 * @author qwy
 * @create 2021-03-31 20:36
 **/
//定义函数式接口
interface Test1{
    int add(int a,int b);
}
//定义普通类,用来被接口函数调用
class MyTest{
    //分静态的方法
    public  int save(int a,int b){
        if(a>b){
            return a+b;
        }else  if(a<b){
            return  a-b;
        }else{
            return a;
        }
    }
}
public class TestLambda {
    public static void main(String[] args) {

        Test1 t1=new MyTest()::save;
        //以下方法也可以
        //Test1 t1=(a,b)->new MyTest().save(a,b);
        int rs = t1.add(3, 4);
        System.out.println("rs = " + rs);
    }
}

运行结果:
rs = -1
注意:引用非静态的方法有两种写法:
1.Test1 t1=new MyTest()::save;
2.Test1 t1=(a,b)->new MyTest().save(a,b);
都可以

3.3. 构造方法的引用

使用场景:
如果某一个函数式接口中定义的方法,仅仅为了得到一个类的对象,此时我们就可以使用构造方法的引用。来简化这个方法的实现。
语法:
类名::new
注意:
可以通过接口中的方法的参数,区分引用不同的构造方法。
实例:

package com.qwy2;

/**
 * @author qwy
 * @create 2021-03-31 21:06
 **/
interface  MyInferface1{
    Person get();
}
interface  MyInferface2{
    Person get(String name,int age);
}
interface  MyInferface3{
    Person get(int id,String name,int age);
}
class Person{
    private int id;
    private String name;
    private int age;

    public Person() {
    }

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

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TestLambda {
    public static void main(String[] args) {
        MyInferface1 m1=Person::new;
        Person p1 = m1.get();
        System.out.println("p1 = " + p1);
        MyInferface2 m2=Person::new;
        Person p2 = m2.get("张三", 12);
        System.out.println("p2 = " + p2);
        MyInferface3 m3=Person::new;
        Person p3 = m3.get(1, "李四", 13);
        System.out.println("p3 = " + p3);
    }
}

运行结果:
p1 = Person{id=0, name=‘null’, age=0}
p2 = Person{id=0, name=‘张三’, age=12}
p3 = Person{id=1, name=‘李四’, age=13}

未完待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值