lamda表达式与函数式接口

发布时间 2023-07-04 17:28:54作者: Acelin_H

认识函数式接口



概述

在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量

比如八大基本类型、自定义实体类等等,以前这些类型都是对客观世界实体的一个数据抽象,而在java7之后,函数也晋升为一等公民。它是对客观实体动作的一个行为抽象。

从我们对接口的定义上来说,我们实现了【用不同的数据,做一样的的事情】到【用不同的数据,做不同的事情】的转变。


 函数式接口只负责定义方法的参数个数、类型以及返回类型,实例化阶段相当于确定方法体。也就是说:当函数式接口被实例化之后,他就跟一个普通方法一样。所有东西都确定了。然后当实例调用了函数式接口里面那个唯一抽象方法之后,就相当于执行了一次该方法

函数式接口定义

何为函数式接口?

一个接口类型的类,有且只有一个抽象方法。

默认方法(default修饰)有实现,所以不是抽象的,隐藏不计入接口的抽象方法个数。

如果一个接口声明了一个抽象方法重写了java.lang的一个公共方法。它也不计入接口的抽象方法计数,因为接口的任何实现都有来自java.lang.Object或其他地方的实现。

@FunctionalInterface  		  // 起标识和校验的作用
public interface MyFunctionInterface {
    
    void run();               // 唯一的抽象方法
    
    default String hello() {  // 默认方法,不计入
        return "hello!";
    }
    
    @Override
    String toString();        // 重写了java.lang的一个公共方法,不计入
}

函数式接口的实例化方式
  • lambda表达式
  • 方法引用
  • 构造方法引用

常见的函数式接口


无参有返回值类型Supplier


适用于没有入参,有返回值的行为抽象

/**
 * 数据提供者。没有要求每次调用提供者时返回一个新的或不同的结果。
 */
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

有参无返回值类型Consumer


适用于接受单个输入参数但不返回结果的行为抽象

/**
 * 数据消费者。表示接受单个输入参数但不返回结果的操作。
 */
@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    /**
     * Consumer对象调用,执行Consumer本身的行为,再执行after本身的行为,也就是一次执行两个	    * 行为
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
二元消费者BiConsumer

适用于需要两个参数,但没有返回结果的行为抽象

/**
 * 表示接受两个输入参数但不返回结果的操作。这就是Consumer的二元专业化。
 */
@FunctionalInterface
public interface BiConsumer<T, U> {


    void accept(T t, U u);

    /**
     * 类似Consumer的andThen()
     */
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);

        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }
}

有参有返回值类型Function

适用于有一个入参,并有返回值的行为抽象

/**
 * 表示接受一个实参并产生结果的函数。
 */
@FunctionalInterface
public interface Function<T, R> {


    R apply(T t);

    /**
     * 入参为一个Function类型函数式接口,接收参数,先执行before行为,将返回值作为入参,执行	 * 本身的行为,然后将返回值返回
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * 入参也是一个Function类型函数式接口,但与compose相反,接收参数,先本身行为,将返回值作	  *	为after行为的入参,执行参数,然后将返回值返回
     * 
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * 将参数直接作为返回值返回
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
BiFunction

适用于需要两个参数,并有返回值的行为抽象

/**
 * 表示接受两个参数并产生结果的函数。这就是Function的二元特殊化。
 */
@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    /**
     * 接收两个参数,执行本身行为,产生的返回值作为Function类型的after的入参,执行after的行	   * 为将after的返回值返回
     */
    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));
    }
}
UnaryOperator

Function特化,返回值类型与参数类型相同

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {

    static <T> UnaryOperator<T> identity() {
        return t -> t;
    }
}

判断类型Predicate


适用于对判定行为的抽象

/**
 * 
 */
@FunctionalInterface
public interface Predicate<T> {


    boolean test(T t);

    /**
     * 本身判断与另一个判断与运算
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * 本身判断非运算
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * 本身判断与另一个判断或运算
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 返回一个谓词,该谓词根据对象测试两个参数是否相等。=(对象,对象)。 参数: targetRef——与	   * 之比较是否相等的对象引用,它可能为空 类型参数:  -谓词实参的类型 返回: 根据对象测试两个      * 参数是否相等的谓词。=(对象,对象) 
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }

其他jdk提供FunctionInterface基本都是这四个类型的特化

应用场景


有入参有返回值
  • 根据id查询name工具类

日常开发的配置表都会有配置的名称,有时候会对一批数据的id查询name返回给前端展示,此时可能会很多重复的id,为提高性能,我们通常的做法就是把id<->name关系放map缓存起来,不同表的id要到对应的表去查,根据【用不同的数据,做不同的事情】,我们可以把查询name的动作给抽象出来,在调用的时候实例化皆可。一下是工具类实现

public final class IdNameHelper {

    private IdNameHelper() {

    }
    
    public static String getName(String id, Map<String, String> idNameMap, UnaryOperator<String> qryNameMethod) {

        Objects.requireNonNull(qryNameMethod);
        String name = idNameMap.get(id);
        if (name == null) {
            name = qryNameMethod.apply(id);
            idNameMap.put(id, name);
        }
        return name;
    }

}
  • 不同的业务,定义不同的操作行为
Map<String, BiFunction<List<ParameterSpec>, List<Map<String, Object>>, Object>>

比如之前一个数据转换器,定义一个map,key为业务类型, value为一个行为,将各个场景的处理方法注册起来,然后根据业务类型执行不同的转逻辑。

  • 发布 - 订阅

其实本质就是数据提供和数据消费,对应的Supplier和Consumer。