Lambda表达式和函数式接口

写在有道笔记中,链接地址。欢迎各位看官提出意见交流讨论

🔗http://note.youdao.com/noteshare?id=147109f1bf7f3ae97c43d77891e6ebc8

 

Lambda表达式和函数式接口

一.Lambda表达式

1.定义

百度百科上如下定义: “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。

2.java中的Lambda

java是一个面向对象的语言,而Lambda表达式却是一个匿名函数,因此java把Lambda表达式抽象成一个匿名内部类(jdk中没有抽象出来,但是它是一个匿名内部类的实现,在下面的截图中,很明显可以看到是一个内部类的地址----【LambdaTestDemo$lambda@679】)。

为了证实如上所说,我就随手写了一个lambda表达式,来调试一下。

package com.gcl.jdk8.lambdatest;

public class LambdaTestDemo1 {

    public static void main(String[] args) {

        //写一个函数接口指向Lambda表达式,主要为了看到lambda表达式在传递时候的状态

        MyFunctionInterface myFunctionInterface = () -> System.out.println("a");

        myFunctionInterface.doSome();
    }
}

@FunctionalInterface
interface MyFunctionInterface{

     void doSome();
}

在debug状态下,我们可以通过watch清楚的看到lambda表达式是一个内部类。清楚了这一点之后,我想对我们理解lambda表达式有着重要的帮助 屏幕快照 2019-05-28 下午10.40.00.png

3.基本结构

我个人将Lambda表达式分为3块,分别是参数模块,->,代码块。类似下面的形式。

 
参数模块
代码块
//真正的表达应该是这样子的
(param1,param2,...,paramn) -> {code body }

那我就分三块来说明一下

a. 参数模块

1.参数类型可以省略

参数模块顾名思义,就是代表着传入Lambda表达式的代码的参数。一般说来我们需要定义传入参数的类型,但是在大部份情况下,jdk会通过上下文去推断这个参数是什么类型,那么就不需要定义了。下面这块代码中,申明 t1的类型的语句可以省略。我在网上查阅了一些人的博客,他们有的把申明类型的语句留着,我想这样的做法第一是跟之前的java的写法保持一致性,第二就是开发的时候虽然jdk可以自己推断,但是开发人员如果还要去推断一下就太麻烦了,大概就是这么些个理由。

2.()可以省略

在参数只有1个的时候,()可以省略。同时也推荐省略。但是如果要申明类型的话,不能省略。

package com.gcl.jdk8.lambdatest;

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

        //写一个函数接口指向Lambda表达式,主要为了看到lambda表达式在传递时候的状态
        Person p = new Person("张三",12);

        //MyFunctionInterface<String> myFunctionInterface = (String t1) -> p.setAge(1);
        MyFunctionInterface<String> myFunctionInterface = t1 -> p.setAge(1);
        
        myFunctionInterface.doSome("a");
    }
}

@FunctionalInterface
interface MyFunctionInterface<T>{

     void doSome(T t);
}


b.->

这个没什么好说的,写一般的Lambda表达式必须写的,这是用来区分参数模块和代码模块的一个标志,但是在特殊Lambda表达式中也是可以写的,在后面我会具体写一写这种特殊的Lambda表达式(方法引用)

c.{} 代码模块

代码模块的具体作用就是处理逻辑的具体实现。 需要注意的是,在具体实现的时候,每个分之都必须满足其返回值,这也好比是函数,规定了返回值类型,如果某一个分之返回与之不合的返回值类型,那就肯定错了。

//例如在函数接口中规定了返回值是范型T,如果返回了其他类型的就出错了
@FunctionalInterface
interface MyFunctionInterface<T>{
     T doSome(T t);
}

public static void main(String[] args) {

        //写一个函数接口指向Lambda表达式,主要为了看到lambda表达式在传递时候的状态
        Person p = new Person("张三", 12);

        MyFunctionInterface<String> myFunctionInterface
                = t1 -> {
            if (t1.equals("true")) {
                return "true";
            } else {
                //return Boolean.FALSE; 这样的返回值就错了
                return "false";
            }
        };

        myFunctionInterface.doSome("a");
    }
 MyFunctionInterface<String> myFunctionInterface2 =  t1 -> t1;

注意:

1.单条语句时候,且是表达式语句(expression)的时候{}可以省略,例如

 MyFunctionInterface<String> myFunctionInterface2 =  t1 -> t1;

2.单条语句时候,且是return语句(statement)的时候{}可以不可以省略,**;**不可以省略,例如

 MyFunctionInterface<String> myFunctionInterface2 =  t1 -> {return t1;};

二. 函数式接口

1.定义: 只有一个抽象方法的接口。

定义十分简单,但是却有一些比较关键的字眼,我抽取其中三个词:一个,抽象,接口。

一个:只有一个抽象方法,多了不行,少了也不行。同时,这个抽象方法一定不能跟Object类中的方法同名。

/**
 * 比如说,MyFunctionInterface这个接口,抽象方法是equal方法,其实他是覆盖了(重写)Object类中的equals方法。加上@FunctionalInterface注解之后,让编译器取做一个显示检查是否这个接口只含有一个抽象方法。这样的接口是一个错误的接口示例。
 */
@FunctionalInterface
interface MyFunctionInterface<T> {

    //根据阿里巴巴java开发手册上的规定,如果是重写的话,强制加上@Override注解
    @Override
    public boolean equals(T t);
}

2.配合Lambda表达式的具体使用

一个自定义函数接口,与之对应的Lambda表达式的参数就是这个默认方法的参数,返回值就是这个默认方法的返回值。


@FunctionalInterface
interface MyFunctionInterface<String> {
    String doSome(String t);
}

  1. 五个常用的函数接口

java提供的函数接口在java.util.function包中,其中比较重要的是以下5个接口。我认为jdk8给我们提供的这些接口是提供了一些模式(帮助我们抽象出来了一些行为),在使用的时候根据官方抽象的接口进行实现也更加方便。

a.Consumer (消费模式)

功能:传递一个参数,无返回

类定义如下:

package java.util.function;

import java.util.Objects;

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * Consumer中的默认方法,表示接受一个参数且没有返回值
     * 
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * 默认方法,提供链式调用方式执行。执行流程:先执行本身的accept在执行传入参数after.accept方法。
     * 该方法会抛出NullPointerException异常。
     * 如果在执行调用链时出现异常,会将异常传递给调用链功能的调用者,且发生异常后的after将不会在调用。
     * 
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

//随手写了一个代码
package com.gcl.jdk8.lambdatest;

import java.util.function.Consumer;

public class ConsumerTest {

    public static void main(String[] args) {

        //普通的Lambda,表示接受一个参数,void 返回值
        Consumer<String> consumer = s -> System.out.println(s);

        //再简化一下,特殊的Lambda表达式,方法引用
        Consumer<String> consumer2 = System.out::println;

        //调用一下,情况如何?
        consumer.accept("a1");

        consumer2.accept("a2");
    }
}

执行结果如下所示,其实就是一个打印字符串的函数,没什么好说的,我们继续下一个接口。

屏幕快照 2019-05-30 下午11.34.37.png

b.Supplier (生产模式)

功能:不传递参数,返回一个值。我们来看一下源码


package java.util.function;

/**
 * Represents a supplier of results.
 * 
   其实就是不接受任何参数,返回这个接口申明的范型,通过get()来进行调用
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

生产者(可以理解为,不接受任何参数,返回一个值,究竟返回什么值由调用者决定----Lambda表达式的制造者)

package com.gcl.jdk8.lambdatest;

import java.util.function.Supplier;

public class SupplierTest {

    public static void main(String[] args) {

        //普通的lambda
        Supplier<String> supplier1 = () -> "nice";

        //特殊的lambda
        Supplier<String> supplier2 = String::new;


        String s1 = supplier1.get();

        String s2 = supplier2.get();

        //打印s1
        System.out.println(s1);

        //打印s2
        System.out.println(s2);
    }
}

c.Function (功能模式)

功能:传递1个参数,返回1个参数。现在依旧来看一下源码

package java.util.function;

import java.util.Objects;

/**
 * Represents a function that accepts one argument and produces a result.
 * 
 * 接受一个参数返回一个结果,T是输入范型,R是输出范型
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

同样是举个例子

package com.gcl.jdk8.lambdatest;

import java.util.function.Function;

public class FunctionTest {

    public static void main(String[] args) {

        //接受一个值,返回一个BooleanFunction<Integer,Boolean> function = (i) -> {
            if(i>1){
                return true;
            } else {
                return false;
            }
        };

        Boolean result = function.apply(3);

        System.out.println(result);
    }
}

在Function<T,R>这个函数式接口中还有着其他方法【compose】,【andThen】,【identity】,这三个方法其实也是其他函数式接口中可能存在的方法(在BiFunction这样的接口中,compose方法是不存在的),对于这样的方法来说在每个函数接口中都是有着自己的实现,只不过我们在读取源码之后发现他在每个函数式接口中的实现都大致相似,因此在Function这个接口中进行一个分析,其他的接口中就不分析了。

1.compose方法,在javadoc中如下描述。 Returns a composed function that first applies the {@code before},其实很简单就是一个å前置执行,需要注意的是它传入的是一个Function接口,返回的也是一个Function接口。

 /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

同样是举个例子

package com.gcl.jdk8.lambdatest;

import java.util.function.Function;

public class FunctionTest {

    public static void main(String[] args) {

        //接受一个值,返回一个BooleanFunction<Integer, Integer> function1 = i -> {
            if (i > 4) {
                return 1;
            } else {
                return 2;
            }
        };
        Function<Integer, Integer> function2 = i -> i * i;
        //在function2执行之前,先执行function1,猜测一下  1小于4返回22*2 = 4
        Integer integer = function2.compose(function1).apply(1);
        System.out.println(integer);


        //在function1执行之前,先执行function2,猜测一下  1*1 = 1 ,4小返回2
        Integer integer2 = function1.compose(function2).apply(1);
        System.out.println(integer2);
    }
}

2.andThen也非常好理解了,就是一个后置执行,通过一个例子看一下。

package com.gcl.jdk8.lambdatest;

import java.util.function.Function;

public class FunctionTest {

    public static void main(String[] args) {

        //接受一个值,返回一个BooleanFunction<Integer, Integer> function1 = i -> {
            if (i > 4) {
                return 1;
            } else {
                return 2;
            }
        };
        Function<Integer, Integer> function2 = i -> i * i;

        //先执行f1,在执行f22*2
        System.out.println(function1.andThen(function2).apply(1));
        //先执行f2,在执行f12   -> 1
        System.out.println(function2.andThen(function1).apply(1));
    }
}

3.identity这个静态方法根据javadoc中的描述,返回的是一个function,返回自己本身。

 /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
package com.gcl.jdk8.lambdatest;

import java.util.function.Function;
import java.util.function.Supplier;

public class FunctionTest {

    public static void main(String[] args) {

        //Supplier去返回一个Functionidentity方法
        Supplier<Function> supplier = Function::identity;

        //输出自己 --  t -> t
        System.out.println(supplier.get().apply(1));


    }
}

d.Predicate (判断模式--谓词)

功能: 传递一个参数,返回一个Boolean值。先看javadoc

package java.util.function;

import java.util.Objects;

/**
 * Represents a predicate (boolean-valued function) of one argument.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #test(Object)}.
 *
 * @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

我在例子中写了一个predicate和一个function,可以看出,pedicate就是一个具体化的function,它规定了它的出参为boolean,为了证实我的想法,我翻阅了java核心技术卷I,第240页,在Predicate的描述中写到,布尔值的函数。当然他也有一个或操作和一些与操作,感兴趣的小伙伴可以去试一下。

package com.gcl.jdk8.lambdatest;

import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FunctionTest {

    public static void main(String[] args) {

        Predicate<Integer> predicate = integer -> {
            if (integer > 3){
                return true;
            } else {
                return false;
            }
        };

        Function<Integer,Boolean> function = integer -> {
            if (integer > 3){
                return true;
            } else {
                return false;
            }
        };
    }
}

e.BiXXXX (BiFunction为例)

功能: 传递两个个参数,得到一个返回值。


import java.util.Objects;

/**
 * Represents a function that accepts two arguments and produces a result.
 * This is the two-arity specialization of {@link Function}.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object, Object)}.
 *
 * @param <T> the type of the first argument to the function
 * @param <U> the type of the second argument to the function
 * @param <R> the type of the result of the function
 *
 * @see Function
 * @since 1.8
 */
@FunctionalInterface
public interface BiFunction<T, U, R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    R apply(T t, U u);

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     */
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
//返回3
public class FunctionTest {

    public static void main(String[] args) {


        BiFunction<Integer,Integer,Integer>  biFunction= (i1,i2) -> i1+i2;

        System.out.println(biFunction.apply(1,2));
    }
}

需要注意的地方是BiFunction没有compose方法,因为BiFunction需要的是两个参数,如果有compsose方法的话,那没有办法在一个方法返回两个返回值。

总结

Lambda表达式和函数式接口分享暂时结束,文章中可能有我理解错误的地方,欢迎大家指正,如果需要分享的话请注明出处,下一次分享就是java8中的流操作。

 

posted @ 2019-05-28 23:06  程序猿灿林  阅读(7250)  评论(2编辑  收藏  举报