Java接口、lambda表达式与内部类——lambda表达式

个人认为该文章对lambda的解释很透彻:https://blog.csdn.net/GoGleTech/article/details/79454151

1.概念:

    lambda表达式是一个可传递的代码块,可以在以后执行一次或多次

2.lambda表达式的语法:

    参数,箭头(->)以及一个表达式

    例如:

    (String first,String second) ->

        {

            if(first.length() < second.length()) return -1;

            else if(first.length() > second.length()) return 1;

            else return 0;

        }

    如果lambda表达式没有参数,仍然要提供空括号,就像无参数方法一样:

    () -> {for(int i = 100 ; i >= 0 ; i--) System.out.println(i);}

    如果可以推导出一个lambda表达式的参数类型,则可以忽略其类型:

    Comparator<String> comp

        =(first,second)

            ->first.length() - second.length();

    编译器可以推导出first和second必然是字符串

    如果方法只有一个参数,而且这个参数的类型可以推导得出,那么甚至还可以省略小括号:

    ActionListener listener = even ->

        System.out.println("The time is" + new Date());

    无需指定lambda表达式的返回类型,lambda表达式的返回类型总是会由上下文推导得出

3.函数式接口:

    推荐博文:https://blog.csdn.net/lz710117239/article/details/7619262

    对于只有一个抽象方法的接口,需要这种的对象时,就可以提供一个lambda表达式,这种接口称为函数式接口

    也就是说有且仅有一个抽象方法,但是Object的public方法除外

    例如Comparator就是只有一个抽象方法的接口,所以可以提供一个lambda表达式:

    Arrays.sort(words ,

        ( first , second ) -> first.length() - second.length());

    在底层,Arrays.sort方法会接收实现了Comparator<String>放某个类的对象。在这个对象上调用compare方法会执行这个lambda表达式

    Java API在java.util.function包中定义了很多非常通用的函数式接口,比如一个接口BiFunction<T,U,R>描述了参数类型为T和U而且返回类型为R的函数

    

    可以把我们的字符串比较lambda表达式保存在这个类型的变量中:

    BiFunction<String,String,Integer> comp

        = (first,second) ->first.length() - second.length();

4.方法引用:

    有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作,这个时候可以直接引用

    例如:

    Arrays.sort(strings,String::compareToIgnoreCase);

    要用::操作符分隔方法名和对象或类名,主要有三种情况:

        object::instanceMethod

        Class::staticMethod

        Class::instanceMethod

    前2中情况中,方法引用等价于提供方法参数的lambda表达式,例如:System.out::println等价于x->System.out.println(x)类似地,Math::pow等价于(x,y)->Math.pow(x,y)

    对于第3种情况,第一个参数会成为方法目标。例如:String::compareToIgnoreCase等同于(x,y)->x.compareToIgnoreCase(y)

    类似于lambda表达式,方法引用不能独立存在,总是会转换为函数式接口的实例

5.构造器引用:

    构造器引用和方法引用很相似,只不过方法名为new,例如Person::new

    也可以用数组类型建立构造器引用,例如:int[]::new

    Java有个限制,无法构造泛型类型T的数组,这个时候构造器引用就很有用:

    Person[] people = stream.toArray(Person[]::new);

6.变量作用域:

    例如:

    public static void repeatMessage(String text,int delay){

        ActionListener listener = event->{

            System.out.println(text);

            Toolkit.getDefaultToolkit().deep();

        };

        new Timer(delay,listener).start();

    }

    此时lambda表达式中的text变量并不是在这个lambda表达式中定义的

    lambda表达式可能会在repeatMessage调用返回很久以后才运行,那个时候这个参数变量可能已经不存在了

    所以如何保留text呢?

    关于代码块以及自由变量值有一个术语:闭包;在Java中,lambda表达式就是闭包

    lambda表达式可以捕获外围作用域中变量的值,只能引用值不会改变的变量,例如下面的做法是不合法的:

    public static void countDown(int start,int delay){

        ActionListener listener = event->{

            start--;

            System.out.println(start);

        };

        new Timer(delay,listener).start();

    }

    因为如果在lambda表达式中改变变量,并发执行多个动作时就会不安全

    另外如果在lambda表达式中引用变量,而这个变量在外部可能改变,这也是不合法的

    总结规则:

    lambda表达式中捕获的变量必须实际上是最终变量

7.处理lambda表达式:

    常用函数式接口:

    函数式接口              参数            返回类型            抽象方法名            描述                                                    其他方法

    Runnable                 无                void                      run                作为无参数或返回值的动作运行

    Supplier<T>            无                  T                         get                提供一个T类型值

    Consumer<T>         T                 void                     accept            处理一个T类型的值                                andThen

    BiConsumer<T,U>  T,U              void                      accept           处理T和U类型的值                                 andThen

    Function<T,R>         T                   R                        apply             有一个T类型参数的函数                        

                                                                                                                                                compose,andThen,identity

    BiFunction<T,U,R>  T,U               R                        apply             有一个T和U类型参数的函数                   andThen

    UnaryOperator<T>  T                  T                         apply             类型T上的一元操作符       compose,andThen,identity

    BinayOperator<T>  T,T                T                         apply             类型T上的二元操作符             andThen,maxBy,minBy

    Predicate<T>            T               boolean                test                布尔值函数                              and,or,negate,isEqual

    BiPredicate<T,U>    T,U             boolean                 test                有两个参数的布尔值                     and,or,negate

    基本类型的函数式接口:

    函数式接口                                    参数类型                    返回类型                            抽象方法名

    BooleanSupplier                            none                        boolean                            getAsBoolean

    PSupplier                                       none                        p                                       getAsP

    PConsumer                                    p                              void                                  accept

    ObjPConsumer<T>                       T,p                            void                                 accept

    PFunction<T>                               p                               T                                       apply

    PToQFunction                               p                               q                                       applyAsQ

    ToPFunction<T>                           T                               p                                       applyAsP

    ToPBiFunction<T,U>                     T,U                           p                                       applyAsP

    PUnaryOperator                            p                              p                                       applyAsP

    PBinayOperator                             p,p                           p                                       applyAsP

    PPredicate                                     p                              boolean                            test

    如果设计你自己的接口,其中只有一个抽象方法,可以用@FunctionalInterface注解来标记这个接口

    这样做有两个优点,如果无意中增加了另一个非抽象方法,编译器会产生一个错误消息,另外javadoc页里会指出你的接口是一个函数式接口

猜你喜欢

转载自blog.csdn.net/qq_38386085/article/details/80816911