【睡JDK】Java函数式编程接口详解之Consumer、Function

今天将介绍Java另外两个函数编程接口Consumer、Function,这两个函数是干嘛的呢?先看看官方的定义:

  • Consumer:表示接受单个输入参数但不返回结果的操作。
  • Function:表示接受一个参数并生成结果的函数。

一、Consumer

1.1 源代码

@FunctionalInterface
public interface Consumer<T> {
    
    
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
    
    
        Objects.requireNonNull(after);
        return (T t) -> {
    
     accept(t); after.accept(t); };
    }
}

1.2 混脸熟

其实Consumer我们经常使用,你看下面这个例子:

List<String> list = Arrays.asList("1", "2", "3");
list.forEach(System.out::println);

我们经常使用的forEach函数其实就是通过Consumer来实现的,所以掌握Consumer很有必要哦,下面看看forEach在ArrayList中的实现:

public void forEach(Consumer<? super E> action) {
    
    
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
    
    
        action.accept(elementData[i]);
    }
    if (modCount != expectedModCount) {
    
    
        throw new ConcurrentModificationException();
    }
}

不用介绍,想必大家也能看的懂,Consumer就表示一个自定义的操作,将该操作作为参数传入到另一个函数内,可在该函数内执行自定义的操作。上面的for循环代码就等同于:

for (int i=0; modCount == expectedModCount && i < size; i++) {
    
    
    System.out.println(elementData[i]);
}

1.3 实现

如果操作比较常用或者通用,可以使用一个类去实现Consumer,保存该操作,在必要的时候能快速使用。

// 实体类
public class Person {
    
    
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
    
    
        this.name = name;
        this.age = age;
    }
    //...
}
// 打印小孩
public class PrintChild implements Consumer<Person> {
    
    
    @Override
    public void accept(Person person) {
    
    
        if (person.getAge() < 18) {
    
    
            System.out.println(person.getName() + "还是个孩子啊");
        }
    }
}

// 测试代码
List<Person> list1 = Arrays.asList(
                new Person("大壮", 19),
                new Person("小光", 16),
                new Person("小小", 15));
list1.forEach(new PrintChild());

1.4 andThen函数介绍

函数源码:

default Consumer<T> andThen(Consumer<? super T> after) {
    
    
    Objects.requireNonNull(after);
    return (T t) -> {
    
     accept(t); after.accept(t); };
}

andThen函数的功能就是将两个Consumer操作合并,并返回一个新的Consumer,使用如下:

// 打印年龄
public class PrintAge implements Consumer<Person> {
    
    
    @Override
    public void accept(Person person) {
    
    
        System.out.println(person.getName() + "的年龄是:" + person.getAge() + "岁");
    }
}

// 测试代码
List<Person> list1 = Arrays.asList(
                new Person("小雨", 19),
                new Person("小光", 16),
                new Person("小小", 15));
list1.forEach(new PrintChild().andThen(new PrintAge()));

// 结果
小雨的年龄是:19岁
小光是个孩子
小光的年龄是:16岁
小小是个孩子
小小的年龄是:15

1.5 Consumer的其他变体

接口名 参数 返回类型 描述
BiConsumer (T, U) void BiConsumer接受两个参数
DoubleConsumer double void 接受一个double类型的参数
IntConsumer int void 接受一个int类型的参数
LongConsumer long void 接受一个long类型的参数
ObjDoubleConsumer (T, double) void 接受一个Object类型和一个double类型参数
ObjIntConsumer (T, int) void 接受一个Object类型和一个int类型参数
ObjLongConsumer (T, long) void 接受一个Object类型和一个long类型参数

二、Function

Function和Consumer在功能上是一致的,但是Function有返回结果。

2.1 源代码

@FunctionalInterface
public interface Function<T, R> {
    
    

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    
    
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    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;
    }
}

2.2 混脸熟

Function我们也是会经常使用到的,喏:

List<Person> list1 = Arrays.asList(
                new Person("小雨", 19),
                new Person("小光", 16),
                new Person("小小", 15));
List<String> list = list1.stream()
    .map(Person::getName)
    .collect(Collectors.toList());

如果你看过笔者写的另一篇文章(重识Java8函数式编程),那么下面的代码你应该也能看得懂,其实上面的map操作可以还原成:

List<String> list = list1.stream()
                .map(person -> {
    
    
                    return person.getName();
                })
                .collect(Collectors.toList());

map内就是封装了一个有返回值的函数,将函数作为参数参入map内。

2.3 compose函数介绍

函数源码:

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    
    
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}

该函数作用就是组合两个Function,参数Function before先执行,执行后的结果交由调用方Function执行。

// 加法
public class AddFunc implements Function<Integer, Integer> {
    
    
    private int origin;

    public AddFunc(int origin) {
    
    
        this.origin = origin;
    }

    @Override
    public Integer apply(Integer integer) {
    
    
        return this.origin + integer;
    }
}

// 减法
public class ReduceFunc implements Function<Integer, Integer> {
    
    
    private int origin;
    private boolean isMinuend;// origin被减数与否

    public ReduceFunc(int origin, boolean isMinuend) {
    
    
        this.origin = origin;
        this.isMinuend = isMinuend;
    }

    @Override
    public Integer apply(Integer integer) {
    
    
        return isMinuend ? this.origin - integer : integer - this.origin;
    }
}

// 测试代码
public class Test {
    
    
    public static void main(String[] args) {
    
    
        // 计算 1 + (2 - 3)
        System.out.println(handle(new AddFunc(1).compose(new ReduceFunc(2, true)), 3));
    }

    public static int handle(Function<Integer, Integer> function, Integer integer) {
    
    
        return function.apply(integer);
    }
}

2.4 andThen函数介绍

函数源码:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    
    
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

andThen函数的作用也是组合两个Function,只不过参数Function后执行。

// 测试代码
public class Test {
    
    
    public static void main(String[] args) {
    
    
        // 计算 1 + 2 - 4
        System.out.println(handle(new AddFunc(1).andThen(new ReduceFunc(4, false)), 2));
    }

    public static int handle(Function<Integer, Integer> function, Integer integer) {
    
    
        return function.apply(integer);
    }
}

2.5 identity函数

源码:

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

好吧,这个函数的功能就是返回一个输入和输出都一样的Function,那么这个函数有什么用呢?看示例:

// 示例:将list转变为map
List<Person> list = Arrays.asList(
                new Person("xiaoxiao", 11),
                new Person("dazhuang", 15));
Map<String, Person> map = list.stream()
    .collect(Collectors.toMap(Person::getName, person -> person));

// 使用identity函数
List<Person> list = Arrays.asList(
                new Person("xiaoxiao", 11),
                new Person("dazhuang", 15));
Map<String, Person> map = list.stream()
    .collect(Collectors.toMap(Person::getName, Function.identity()));

两种使用方式都是可以的,但是你觉得哪个方式更有魅(bi)力(ge)呢?

2.6 我们能做啥

  • 掌握了Function,在编写公共组件逻辑的时候,你可以将部分逻辑上抛,由调用者去实现自己的特性,能增强组件的灵活性。
  • 实现Function接口,定义一些常用操作,减少代码的冗余。

运用的基础是先掌握,只有掌握才能熟练运用,干了这杯毒鸡汤。(小声BB:端午最后一天假了,T . T)

2.7 Function的其他变体

接口名 参数 返回类型 描述
BiFunction (T, U) R BiFunction接受两个参数
DoubleFunction double R 接受一个double类型的参数
DoubleToIntFunction double int 接受double类型参数,返回int类型结果
DoubleToLongFunction double long 接受double类型参数,返回long类型结果
IntFunction int R 接受一个int类型的参数
IntToDoubleFunction int double 接受int类型参数,返回double类型结果
IntToLongFunction int long 接受int类型参数,返回long类型结果
LongFunction long R 接受一个long类型的参数
LongToDoubleFunction long double 接受long类型参数,返回double类型结果
LongToIntFunction long int 接受long类型参数,返回int类型结果
ToDoubleBiFunction (T, U) double 接受两个参数,返回double类型结果
ToDoubleFunction T double 接受一个Object类型,返回double类型参数
ToIntBiFunction (T, U) int 接受两个参数,返回int类型结果
ToIntFunction T int 接受一个Object类型,返回int类型参数
ToLongBiFunction (T, U) long 接受两个参数,返回long类型结果
ToLongFunction T long 接受一个Object类型,返回long类型参数

文章推荐:

【睡JDK】Java函数式编程接口详解之Predicate

【睡JDK】Java函数式编程接口详解之Supplier

【睡JDK】Java函数式编程接口详解之UnaryOperator、BinaryOperator

end
Java开发乐园

猜你喜欢

转载自blog.csdn.net/u012534326/article/details/107031007