Java之Lambda表达式

132 阅读6分钟

一、Lambda表达式简介

什么是Lambda?

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

为什么使用Lambda

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

Lambda对接口的要求

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

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

@FunctionalInterface

该注解修饰函数式接口的,即接口中的抽象方法只有一个

二、Lambda的基础语法

1.语法

Lambda是一个匿名函数 一般关注的是以下两个重点:参数列表 方法体

  • ():用来描述参数列表
  • {}:用来描述方法体,有时可以省略
  • ->: Lambda运算符,读作goes to

2.创建不同类型的接口

/**
 * 无参数无返回值接口
 */
@FunctionalInterface
public interface LambdaNoneReturnNoneParmeter {

    void test();
}


/**
 * 无返回值有单个参数
 */
@FunctionalInterface
public interface LambdaNoneReturnSingleParmeter {

    void test(int n);
}


/**
 * 无返回值 多个参数的接口
 */
@FunctionalInterface
public interface LambdaNoneReturnMutipleParmeter {

    void test(int a,int b);
}


/**
 * 有返回值 无参数接口
 */
@FunctionalInterface
public interface LambdaSingleReturnNoneParmeter {

    int test();
}


/**
 * 有返回值 有单个参数的接口
 */
@FunctionalInterface
public interface LambdaSingleReturnSingleParmeter {

    int test(int n);
}


/**
 * 有返回值 有多个参数的接口
 */
@FunctionalInterface
public interface LambdaSingleReturnMutipleParmeter {

    int test(int a,int b);
}

3.创建测试类

public class Syntax1 {

    public static void main(String[] args) {
        // 1.Lambda表达式的基础语法
        // Lambda是一个匿名函数 一般关注的是以下两个重点
        // 参数列表 方法体

        /**
         * ():用来描述参数列表
         *  {}:用来描述方法体
         *  ->: Lambda运算符 读作goes to
         */

        // 无参无返回  
        LambdaNoneReturnNoneParmeter lambda1=()->{
            System.out.println("hello word");
        };
        // 调用
        lambda1.test();

        
        // 无返回值 单个参数 
        LambdaNoneReturnSingleParmeter lambda2=(int n)->{
            System.out.println("参数是:"+n);
        };
        // 调用
        lambda2.test(10);

        // 无返回值 多个参数
        LambdaNoneReturnMutipleParmeter lambda3=(int a,int b)->{
            System.out.println("参数和是:"+(a+b));
        };
        // 调用
        lambda3.test(10,12);

        // 有返回值 无参数
        LambdaSingleReturnNoneParmeter lambda4=()->{
            System.out.println("lambda4:");
            return 100;
        };
        // 调用,有返回值
        int ret=lambda4.test();
        System.out.println("返回值是:"+ret);

        // 有返回值 单个参数
        LambdaSingleReturnSingleParmeter lambda5=(int a)->{
            return a*2;
        };
        // 调用,有返回值
        int ret2= lambda5.test(3);
        System.out.println("单个参数,lambda5返回值是:"+ret2);

        //有返回值 多个参数
        LambdaSingleReturnMutipleParmeter lambda6=(int a,int b)->{
            return a+b;
        };
        // 调用,有返回值
        int ret3=lambda6.test(12,14);
        System.out.println("多个参数,lambda6返回值是:"+ret3);
    }
}

输出结果:
        hello word
	参数是:10
	参数和是:22
	lambda4:
	返回值是:100
	单个参数,lambda5返回值是:6
        多个参数,lambda6返回值是:26

三、语法精简

1.参数类型精简

语法精简 参数类型:

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


// 可以精简为
LambdaNoneReturnMutipleParmeter lambda1=(a,b)-> {
    System.out.println("hello world");
};

2.参数小括号精简

参数小括号 如果参数列表中,参数的数量只有一个 此时小括号可以省略

LambdaNoneReturnSingleParmeter lambda2=(a)->{
    System.out.println("hello world");
};

可以精简为:
LambdaNoneReturnSingleParmeter lambda2= a->{
    System.out.println("hello world");
};

3.方法大括号精简

方法大括号 如果方法体中只有一条语句,此时大括号可以省略

LambdaNoneReturnSingleParmeter lambda3=a->{
    System.out.println("hello world");
};

可以精简为:
LambdaNoneReturnSingleParmeter lambda3=a->System.out.println("hello world");

4.大括号精简

如果方法体中唯一的一条语句是一个返回语句 贼省略大括号的同时 也必须省略return

LambdaSingleReturnNoneParmeter lambda4=()->{
    return 10;
};

可以精简为:
LambdaSingleReturnNoneParmeter lambda4=()->10;

5.多参数,有返回值精简

LambdaSingleReturnNoneParmeter lambda4=(a,b)->{
    return a+b;
};

可以精简为:
LambdaSingleReturnMutipleParmeter lambda5=(a,b)->a+b;

2.Lambda方法引用

1.普通方法与静态方法

在实际应用过程中,一个接口在很多地方都会调用同一个实现

LambdaSingleReturnMutipleParmeter lambda1=(a,b)->a+b;
LambdaSingleReturnMutipleParmeter lambda2=(a,b)->a+b;

这样一来每次都要写上具体的实现方法 a+b,如果需求变更,则每一处实现都需要更改,基于这种情况,可以将后续的是实现更改为已定义的方法,需要时直接调用就行。

方法引用:

  • 可以快速地将一个Lambda表达式的实现指向一个已经实现的方法。
  • 方法的隶属者,如果是静态方法,隶属的就是一个类,如果是普通方法,就是隶属对象
  • 引用的方法中,参数数量和类型一定要和接口中定义的方法一致。
  • 返回值的类型也一定要和接口中的方法一致。
public class Syntax3 {

    public static void main(String[] args) {
        
        LambdaSingleReturnSingleParmeter lambda1=a->a*2;
        LambdaSingleReturnSingleParmeter lambda2=a->a*2;
        LambdaSingleReturnSingleParmeter lambda3=a->a*2;

        // 简化
        LambdaSingleReturnSingleParmeter lambda4=a->change(a);

        // 静态方法引用
        LambdaSingleReturnSingleParmeter lambda5=Syntax3::change;
    }

    /**
    * 自定义的实现方法
    */
    private static int change(int a){
        return a*2;
    }
}

2.构造方法

实体类

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的有参构造方法执行");
    }
}

两个接口:一个接口的方法需要引用Person的无参构造,一个接口的方法需要引用Person的有参构造 用于返回两个Person对象。

interface PersonCreater{
    //通过Person的无参构造实现
    Person getPerson();
}

interface PersonCreater2{
    //通过Person的有参构造实现
    Person getPerson(String name,int age);
}

Lambda表达式

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

        PersonCreater creater=()->new Person();

         // 引用的是Person的无参构造
         // PersonCreater接口的方法指向的是Person的方法
        PersonCreater creater1=Person::new; //等价于上面的()->new Person()
        
        // 实际调用的是Person的无参构造 相当于把接口里的getPerson()重写成new Person()。
        Person a=creater1.getPerson(); 

        // 引用的是Person的有参构造
        PersonCreater2 creater2=Person::new;
        Person b=creater2.getPerson("张三",18);
    }
}

是引用无参构造还是引用有参构造 在于接口定义的方法参数

四、Lambda闭包

/**
 * @author Alan
 * @version 1.0
 * @date 2020-05-27 16:59
 */
public class ClosureDemo {
    public static void main(String[] args) {

        /**
         * lambda的闭包会提升包围变量的生命周期
         * 所以局部变量 num在getNumber()方法内被 get()引用 不会在getNumber()方法执行后销毁
         * 这种方法可以在外部获取到某一个方法的局部变量
         */
        int n=getNumber().get();
        System.out.println(n);
    }
    private static Supplier<Integer> getNumber(){
        int num=10;
        /**
         * Supplier supplier=()->num;
         * return supplier;
         */
        return ()->{
            return num;
        };
    }
}


*************************************************************************


public class ClosureDemo2 {
    public static void main(String[] args) {
        int a=10;
        Consumer<Integer> c=ele->{
            System.out.println(a+1);
            //System.out.println(ele);
            //System.out.println(a++); 会报错
            //在lambda中引用局部变量 这个变量必须是一个常量
        };
        //a++; 这样也会导致内部报错
        //如果在内部已经引用局部变量 参数传递后 打印的还是 10
        c.accept(1);
    }
}

参考资料:www.bilibili.com/video/BV164…