睁眼写BUG,闭眼改BUG。

Lambda 表达式

2020.08.08

Lambda 表达式

简介

Java8新特性,从本质上讲,是一个匿名函数。可以使用这个匿名函数,实现接口中的方法。对接口进行非常简洁的实现,从而简化代码。

简化接口的实现

使用场景

关于接口的实现,可以有很多种方式来实现。例如:设计接口的实现类、使用匿名内部类。但是lambad表达式,比这两种方式都简单。

Lambda表达式对接口的要求

并不是所有的接口都可以使用lambda表达式来简洁实现的。

当实现的接口中的方法过多或过少的时候,lambda表达式都是不适用的。

lambda表达式,只能实现函数式接口

函数式接口

基础概念

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

    // 这个接口中,有且只有一个方法,是实现类必须实现的,因此是一个函数式接口。
    interface Test1 {
        void test(); // I have one.
    }

    // 这个接口中,实现类必须要实现的方法,有两个!因此不是一个函数式接口。
    interface Test2 {
        void test1(); //I have two methods to implement.
        void test2();
    }

    // 无需要实现的方法!因此不是函数式接口。
    interface Test3 {
        // I don't have an method to implement.
    }

    // 没有定义任何方法,但继承了Test1,可以从父接口继承到一个抽象的方法。是一个函数式接口。
    interface Test4 extends Test1 {
        // inherit 继承;define 定义;
        // I did not define any methods, but I inherited Test1
    }

    // 这个接口定义了两个方法,但是default方法子类不是必须实现的。
    // 因此,实现类实现这个接口的时候,必须实现的方法只有一个!是一个函数式接口。
    interface Test5 {
        // although 虽然
        void test5(); // Although I have defined two methods, only one method needs to be implemented.
        default void test() {}
    }

    // 这个接口中的 toString 方法,是Object类中定义的方法。
    // 此时,实现类在实现接口的时候,toString可以不重写!因为可以从父类Object中继承到!
    // 此时,实现类在实现接口的时候,有且只有一个方法是必须要重写的。是一个函数式接口。
    interface Test6 {
        void test6(); // Although I have defined two methods, there is one method that can not be implemented.
        String toString();
    }
    
    // 不是函数式接口,因为 toString 可以重写,可以不重写
    interface Test7 {
        String toString();
    }
    
    // 函数式接口
    interface Test8 {
        void test();
        default void test1() {}
        static void test2() {}
        String toString();
    }

@FunctionalInterface

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

    @FunctionalInterface
    interface Test1 {
        void test(); 
    }

系统内置的若干函数式接口

接口名字参数返回值特殊接口
PredicateTbooleanIntPredicate: 参数 int,返回值 boolean
LongPredicate: 参数 long 返回值 boolean
DoublePredicate: 参数 double返回值 boolean
ConsumerTvoidIntConsumer: 参数 int,返回值 void
LongConsumer: 参数 long 返回值 void
DoubleConsumer: 参数 double返回值 void
FunctionTR...
SupplierT...
UnaryOperatorTT...
BinaryOperatorT, TT...
BiPredicate<L,R>L,Rboolean
BiConsumer<T,U>T,Uvoid
BiFuction<T,U,R>T,UR

基础语法

lambda表达式,其实本质来讲,就是一个匿名函数。因此在写lambda表达式的时候,不需要关心方法名是什么,也不需要关心返回值类型。只需要关注:参数列表方法体

基础语法:

(参数) -> {
    方法体
};

参数部分:方法的参数列表,要求和实现接口中的方法参数部分一直,包括参数的数量和类型。

方法体部分:方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。

->:分隔参数部分和方法体部分。

        // 实现有多参数,有返回值的函数式接口
        SingleReturnMultipleParameter lambda6 = (int a, int b) -> {
            return a + b;
        };

参数的精简

  • 参数的类型
    • 由于在接口的方法中,已经定义了每个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类型需要和接口中的方法保持一致。因此,此时lambda表达式中的参数的类型可以省略不写。
    • 注意事项
      • 如果需要省略参数的类型,要保证:要省略,每一个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略,有的参数类型没有省略。
        // 实现有多参数,有返回值的函数式接口
        SingleReturnMultipleParameter lambda6 = (a, b) -> {
            return a + b;
        };
  • 参数的小括号
    • 如果方法的参数列表中的参数数量 有且只有一个,此时,参数列表的小括号是可以省略不写的。
    • 注意事项:
      • 只有当参数的数量是一个的时候
      • 省略掉小括号同时必须省略参数的类型
        // 实现有一个参数,有返回值的函数式接口
        SingleReturnSingleParameter lambda5 =  a -> {
            return a;
        };

方法体的精简

// 省略 return
SingleReturnSingleParameter lambda5 =  a -> a;

// 省略大括号
NoneReturnNoneParameter lambda1 = () -> System.out.println("This is a method with no parameters and no return value");