Java8 Function,Consumer,Supplier
有关 JDK8 新特性之前写了三篇博客:
1,java 代码之美 (1)---Java8 Lambda
2,java 代码之美 (2)---Java8 Stream
3,java 代码之美 (13)--- Predicate 详解
这一篇我们来了解 JDK8 已经定义好的几个函数式接口.
一, 概述
Jdk8 之后新增的一个重要的包 : java.util.function
该包下所有的接口都是函数式接口, 按分类主要分为四大接口类型: Function,Consumer,Predicate,Supplier. 有关 Predicate 这里不再讲解, 因为上面有单独写过一篇博客.
延伸如下
这里也仅仅是展示一部分, 我们看看 java.util.function 包下
二, Consumer
作用 一听这名字就知道是消费某个对象, 没有返回值.
1, 源码
在源码中只有两个方法, 一个抽象方法, 一个默认方法.
- @FunctionalInterface
- public interface Consumer<T> {
- /**
- * 抽象方法: 传入一个指定泛型的参数, 无返回值
- */
- void accept(T t);
- /**
- * 如同方法名字一样 andThen, 类似一种相加的功能 (下面会举例说明)
- */
- default Consumer<T> andThen(Consumer<? super T> after) {
- Objects.requireNonNull(after);
- return (T t) -> { accept(t); after.accept(t); };
- }
- }
2, 使用示例
- public static void main(String[] args) {
- testConsumer();
- testAndThen();
- }
- /**
- * 一个简单的平方计算
- */
- public static void testConsumer() {
- // 设置好 Consumer 实现方法
- Consumer<Integer> square = x -> System.out.println("平方计算 :" + x * x);
- // 传入值
- square.accept(2);
- }
- /**
- * 定义 3 个 Consumer 并按顺序进行调用 andThen 方法
- */
- public static void testAndThen() {
- // 当前值
- Consumer<Integer> consumer1 = x -> System.out.println("当前值 :" + x);
- // 相加
- Consumer<Integer> consumer2 = x -> { System.out.println("相加 :" + (x + x)); };
- // 相乘
- Consumer<Integer> consumer3 = x -> System.out.println("相乘 :" + x * x);
- //andThen 拼接
- consumer1.andThen(consumer2).andThen(consumer3).accept(1);
- }
运行结果
单个这样消费看去并没啥意义, 但如果是集合操作就有意义了, 所以 Jdk8 的 Iterator 接口就引入了 Consumer.
3,JDK8 使用
Iterable 接口的 forEach 方法需要传入 Consumer, 大部分集合类都实现了该接口, 用于返回 Iterator 对象进行迭代.
- public interface Iterable<T> {
- //forEach 方法传入的就是 Consumer
- default void forEach(Consumer<? super T> action) {
- Objects.requireNonNull(action);
- for (T t : this) {
- action.accept(t);
- }
- }
- }
我们在看给我们带来的便利
- public static void main(String[] args) {
- // 假设这里有个集合, 集合里的对象有个 status 属性, 现在我想对这个属性赋值一个固定值
- List<Pension> pensionList = new ArrayList<>();
- //1, 传统的通过 for 循环添加
- for (Pension pension : pensionList) {
- pension.setStatus(1);
- }
- //2, 通过 forEach 的 Consumer 添加
- pensionList.forEach(x -> x.setStatus(1));
- }
这样一比较是不是代码简洁了点, 这就是 Consumer 是我们代码带来简洁的地方.
三, Supplier
作用 提前定义可能返回的一个指定类型结果, 等需要调用的时候再获取结果.
1, 源码
- @FunctionalInterface
- public interface Supplier<T> {
- /**
- * 只有这一个抽象类
- */
- T get();
- }
源码非常简单.
2,JDK8 使用
在 JDK8 中 Optional 对象有使用到
Optional.orElseGet(Supplier<? extends T>) // 当 this 对象为 null, 就通过传入 supplier 创建一个 T 返回.
我们看下源码
- public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
- if (value != null) {
- return value;
- } else {
- throw exceptionSupplier.get();
- }
- }
使用示例
- public static void main(String[] args) {
- Person son = null;
- // 先判断 son 是否为 null, 如果为不为 null 则返回当前对象, 如果为 null 则返回新创建的对象
- BrandDTO optional = Optional.ofNullable(son).orElseGet(() -> new Person());
- }
这样代码是不是又简单了. 有关 Optional 这里就不多说, 接下来会单独写一篇博客.
四, Function
作用 实现一个 "一元函数", 即传入一个值经过函数的计算返回另一个值.
1, 源码
- @FunctionalInterface
- public interface Function<T, R> {
- /**
- * 抽象方法: 根据一个数据类型 T 加工得到一个数据类型 R
- */
- R apply(T t);
- /**
- * 组合函数, 调用当前 function 之前调用
- */
- default <V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T> before) {
- Objects.requireNonNull(before);
- return (V v) -> apply(before.apply(v));
- }
- /**
- * 组合函数, 调用当前 function 之后调用
- */
- default <V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V> after) {
- Objects.requireNonNull(after);
- return (T t) -> after.apply(apply(t));
- }
- /**
- * 静态方法, 返回与原函数参数一致的结果. x=y
- */
- static <T> java.util.function.Function<T, T> identity() {
- return t -> t;
- }
- }
2, 使用示例
- public static void main(String[] args) {
- applyTest();
- andThenTest();
- composeTest();
- test();
- }
- /**
- * 1,apply 示例
- */
- private static void applyTest() {
- // 示例 1: 定义一个 funciton, 实现将 String 转换为 Integer
- Function<String, Integer> function = x -> Integer.parseInt(x);
- Integer a = function.apply("100");
- System.out.println(a.getClass());
- // 结果: class java.lang.Integer
- }
- /**
- * 2,andThen 示例
- */
- private static void andThenTest() {
- // 示例 2: 使用 andThen() 实现一个函数 y=10x + 10;
- // 先执行 10 * x
- Function<Integer, Integer> function2 = x -> 10 * x;
- // 通过 andThen 在执行 这里的 x 就等于上面的 10 * x 的值
- function2 = function2.andThen(x -> x + 10);
- System.out.println(function2.apply(2));
- // 结果: 30
- }
- /**
- * 3,compose 示例
- */
- private static void composeTest() {
- // 示例 3: 使用 compose() 实现一个函数 y=(10+x)2;
- Function<Integer, Integer> function3 = x -> x * 2;
- // 先执行 x+10 在执行 (x+10)*2 顺序与上面相反
- function3 = function3.compose(x -> x + 10);
- System.out.println(function3.apply(3));
- // 结果: 26
- }
- /**
- * 4, 综合示例
- */
- private static void test() {
- // 示例 4: 使用使用 compose(),andThen() 实现一个函数 y=(10+x)*2+10;
- // 执行第二步
- Function<Integer, Integer> function4 = x -> x * 2;
- // 执行第一步
- function4 = function4.compose(x -> x + 10);
- // 执行第三步
- function4 = function4.andThen(x -> x + 10);
- System.out.println(function4.apply(3));
- // 结果: 36
- }
3,JDK8 使用
有两个地方很常用
- ,V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码, 如果指定的键尚未与值关联或与 null 关联, 使用函数返回值替换.
- ,<R> Stream<R> map(Function<? super T, ? extends R> mapper); // 转换流
computeIfAbsent 使用示例
- Map<String, List<String>> map = new HashMap<>();
- List<String> list;
- // java8 之前写法
- list = map.get("key");
- if (list == null) {
- list = new LinkedList<>();
- map.put("key", list);
- }
- list.add("11");
- // 使用 computeIfAbsent 可以这样写 如果 key 返回部位空则返回该集合 , 为空则创建集合后返回
- list = map.computeIfAbsent("key", k -> new ArrayList<>());
- list.add("11");
stream 中 map 使用示例
- public static void main(String[] args) {
- List<Person> persionList = new ArrayList<Person>();
- persionList.add(new Person(1,"张三","男",38));
- persionList.add(new Person(2,"小小","女",2));
- persionList.add(new Person(3,"李四","男",65));
- //1, 只取出该集合中所有姓名组成一个新集合 (将 Person 对象转为 String 对象)
- List<String> nameList=persionList.stream().map(Person::getName).collect(Collectors.toList());
- System.out.println(nameList.toString());
- }
代码是不是又简洁了.
总结 这些函数式接口作用在我看来, 就是定义一个方法, 方法中有个参数是函数式接口, 这样的话函数的具体实现则由调用者来实现. 这就是函数式接口的意义所在.
一般我们也会很少去定义一个方法, 方法参数包含函数接口. 我们更重要的是学会使用 JDk8 中带有函数式接口参数的方法, 来简化我们的代码.
参考
1,JDK1.8 函数式接口 Function,Consumer,Predicate,Supplier https://www.jianshu.com/p/9b6396a889e4
2,JAVA 8 函数式接口
你如果愿意有所作为, 就必须有始有终.(25)
来源: https://www.cnblogs.com/qdhxhz/p/12050701.html