编程技术文章分享与教程

网站首页 > 技术文章 正文

Java8中的函数式接口Supplier、Consumer、BiConsumer详解

hmc789 2024-11-16 20:56:36 技术文章 2 ℃

目录

一、什么是函数式接口?

二、函数式接口应用实战

1. BiConsumer接口和Consumer接口

1) accept(T t,U u)方法

2) andThen(BiConsumer)方法

2. Supplier接口

3. Supplier接口和Consumer接口结合应用实战

1) 定义of()方法创建类的对象

2) 定义with()回调属性赋值

3) 定义build()方法开始回调

4) 测试

5) 调用过程解读

6) 完整代码



函数式编程是JDK1.8的新特性,函数式接口是函数式编程主要的体现,在有些情况下我们可以用lambda表达式替代函数式接口传参。


首先使用hashmap的computeIfAbsent方法举个栗子,该方法的作用是从hashmap中根据key获取一个值,如果没有值, 那么就给该key填充一个值。


   Map<String, Integer> userMap = new HashMap<>();
        Integer value = userMap.computeIfAbsent("1", new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return 1;
            }
        });


我们可以看到Funtion接口以参数的形式传给了computeIfAbsent方法,在apply()方法里Return了1, 得到value的值为1,因为computeIfAbsent方法在get一个不存在的key时,会通过接口对象来调用apply()方法,然后将apply()方法的结果返回并将key和value存入到map里。


    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }


使用lambda表达式替换,结果是一样的:


package func;

import java.util.HashMap;
import java.util.Map;

public class FunctionTests {


    public static void main(String[] args) {
        Map<String, Integer> userMap = new HashMap<>();
        Integer value = userMap.computeIfAbsent("1", s -> 1);
        if (userMap.get("1").equals(value)) {
            System.out.println("yes!");
        }

    }
}



lamda表达式看起来更简洁一点。


一、什么是函数式接口?


函数式接口是java8中新加入的一批接口,他们由@FutionalInterface注解标记的接口,我们可以在rt.jar包下的java.util.function包里找到有很多函数式接口,比如常用的函数式接口有Function、Supplier、consumer和Prdicate等。



可以发现,函数式接口里只允许一个抽象方法,我们也可以通过@FunctionalInterface注解自定义一个函数式接口。


@FunctionalInterface
public interface Function0<T> {

    T get();
    
}


如果包含多个抽象方法,那么会出现报错的提示:



二、函数式接口应用实战


1. BiConsumer接口和Consumer接口


BiConsumer定义了两个泛型类型T和R,分别做为accept()方法的参数类型,BiConsumer支持2个参数。


@FunctionalInterface
public interface BiConsumer<T, U> {

    /**
     * Performs this operation on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     */
    void accept(T t, U u);

    /**
     * Returns a composed {@code BiConsumer} 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.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code BiConsumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);

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


1) accept(T t,U u)方法


accept方法能接受2个类型的参数,例如我们可以在accept方法里写一些逻辑,利用参数t和u做一些事情:


package java8.func;


import java.util.function.BiConsumer;

/**
 * 测试java.uti.function.BiConsumer接口
 */
public class BiConsumerTests {

    static class Record<T, R> {

        private final BiConsumer<T, R> biConsumer;


        // 初始化biConsumer
        public Record(BiConsumer<T, R> biConsumer) {
            this.biConsumer = biConsumer;
        }


        // 调用accept方法
        public void consume(T t, R r) {
            biConsumer.accept(t, r);
        }

        private Integer value;

        public Integer getValue() {
            return value;
        }

        public void setValue(Integer value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        Record<String, Integer> record = new Record<>((k, v) -> {
            System.out.println(k+","+v);
        });
        record.consume("1", 1);

    }
}



2) andThen(BiConsumer<? super T,? super U>)方法


<? super T, ? super U> super表示下限的类型,extends 表示上限的类型,参数1 至少为T类型或者为T类型的父类,参数2为U类型或者U类型的父类。andThen方法表示会继承当前BiConsumer继续调用下一个BiConsumer里的accept()方法。


下面使用andThen 结合accept实现一个广播功能,一次Accept,多个注册的consumer都能收到消息。


package java8.func;


import java.util.function.BiConsumer;

/**
 * 测试java.uti.function.BiConsumer接口
 */
public class BiConsumerTests {

    static class Record<T, R> {

        private final BiConsumer<T, R> biConsumer;

        private Record<T, R> next;


        // 初始化biConsumer
        public Record(BiConsumer<T, R> biConsumer) {
            this.biConsumer = biConsumer;
        }


        // 调用accept方法,多个biConsumer
        public void consume(T t, R r) {
            //当前
            BiConsumer<T, R> chainConsumer = this.biConsumer;
            while (this.next != null) {
                chainConsumer = chainConsumer.andThen(this.next.biConsumer);
                next = next.next;
            }
            chainConsumer.accept(t, r);
        }

        public void addLast(BiConsumer<T, R> biConsumer) {
            // 遍历链表到表尾
            Record<T,R> current=this;
            while (current.next != null) {
                current = current.next;
            }
            current.next = new Record<>(biConsumer);
        }


        private Integer value;

        public Integer getValue() {
            return value;
        }

        public void setValue(Integer value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        Record<String, Integer> record = new Record<>((k, v) -> {
            System.out.println(k + "," + v);
        });

        record.addLast((k, v) -> {
            System.out.println(k + "-" + v);
        });

        record.addLast((k, v) -> {
            System.out.println(k + "+" + v);
        });
        record.consume("1", 1);

    }
}


打印结果:



BiConsumer和Consumer区别在于Accept方法入参的个数,consumer里的accept方法只支持一个T类型的参数。


2. Supplier接口


Supplier接口比consumer接口要更简单点,只有一个get()方法,该方法没有任何参数,也没有andThen,可以通过Supplier给一个类里的属性进行初始化,如下通过supplier给一个List<T>进行初始化。


package java8.func;

import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;

public class SupplierTests {


    static class Record<T> {


        private final Supplier<List<T>> supplier;


        public Record(Supplier<List<T>> supplier) {
            this.supplier = supplier;
        }


        public List<T> getAllRecords() {
            return supplier.get();
        }
    }

    public static void main(String[] args) {


        Record<Integer> record = new Record<>(() -> Arrays.asList(1, 2, 3, 4, 5));
        List<Integer> records = record.getAllRecords();
        System.out.println(records);
    }
}


打印结果:



3. Supplier接口和Consumer接口结合应用实战


我们可以利用Supplier和Consumer接口编写一个类的初始化工具类,用一行代码去替代不断的set属性。


1) 定义of()方法创建类的对象


通过supplier将类的对象给保存下来。


    public static <T> GenericBuilder<T> of(Supplier<T> type) {
        return new GenericBuilder<>(type);
    }


2) 定义with()回调属性赋值


这里用一个List去保存所有的with,就是说类的属性有多个赋值。


    // T 为类的对象,U为值的属性的
    public <U> GenericBuilder<T> with(BiConsumer<T, U> biConsumer, U value) {
        //这一行代码为什么要这样写? 因为我们可以先将用accept方法将Consumer放入到typeModifier列表里
        //当 调用tConsumer.accept(typeInstance)时, typeInstance对象会传入到每一个consumer的accept方法里
        // 调用了biConsumer.accept(t,value)方法,就相当于调用了user.setUserName("bing"), user.setPassWord(bing)方法

//        Consumer<T> tConsumer =new Consumer<T>() {
//            @Override
//            public void accept(T t) {
//                biConsumer.accept(t,value);
//            }
//        };
        Consumer<T> tConsumer = t -> biConsumer.accept(t, value);
        this.typeModifier.add(tConsumer);
        return this;
    }


3) 定义build()方法开始回调


因为我们用set方法去赋值时,需要用到的是同一个对象,因此我们可以把supplier传入给tConsumer.accept()方法。


  public T build() {
        T typeInstance = this.type.get();
        // 给所有的consumer
        this.typeModifier.forEach(tConsumer -> {
            tConsumer.accept(typeInstance);
        });
        this.typeModifier.clear();
        return typeInstance;
    }


4) 测试


package java8.func;

import java.util.function.BiConsumer;

public class TypeBuilderTest {

    static class User {

        private Integer id;
        private String userName;

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", userName='" + userName + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
//        User user = TypeBuilder.of(User::new)
//                .with(User::setId, 1)
//                .with(User::setUserName, "bing")
//                .build();

        User user = TypeBuilder.of(User::new)
                .with(new BiConsumer<User, Integer>() {
                    @Override
                    public void accept(User user, Integer integer) {
                        user.setId(integer);
                    }
                }, 1)
                .with(User::setUserName, "bing")
                .build();
        System.out.println(user);
    }
}


打印结果:



可以发现我们只需要不停地with就可以实现频繁的with赋值了,是不是变得更简洁了!


5)调用过程解读



6) 完整代码


package java8.func;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class TypeBuilder<T> {

    private final Supplier<T> type;

    private List<Consumer<T>> typeModifierLists = new ArrayList<>();

    public TypeBuilder(Supplier<T> type) {
        this.type = type;
    }


    public static <T> TypeBuilder<T> of(Supplier<T> type) {
        return new TypeBuilder<T>(type);
    }

    //将属性设置组装成Consumer。
    public <U> TypeBuilder<T> with(BiConsumer<T, U> biConsumer, U value) {
//        Consumer<T> consumer = t -> biConsumer.accept(t, value);
        Consumer<T> consumer = new Consumer<T>() {
            @Override
            public void accept(T t) {
                // 回调目标方法
                biConsumer.accept(t, value);
            }
        };
        this.typeModifierLists.add(consumer);
        return this;
    }


    public T build() {
        T type = this.type.get();
        // 将type传给所有的consumer, consumer接收到type后,执行biConsumer.accept(t,value), 然后回调setUserName。
//        this.typeModifierLists.forEach(tConsumer -> tConsumer.accept(type));
        this.typeModifierLists.forEach(new Consumer<Consumer<T>>() {
            @Override
            public void accept(Consumer<T> tConsumer) {
                tConsumer.accept(type);
            }
        });
        this.typeModifierLists.clear();
        return type;
    }


}
标签列表
最新留言