三、Java8四大内置核心函数式接口

    首先总览下四大函数式接口的特点说明:

    java.util.function.Consumer<T> 接口是消费一个数据,其数据类型由泛型决定。

    接口源码:

    package java.util.function;

    import java.util.Objects;

    @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); };
        }
    }
    public class ConsumerTest {

     /**
      * 先计算总分,再计算平均分
      */

     @Test
     public void calculate() {
      Integer[] fraction = new Integer[] { 657685928899 };
      consumer(fraction, x -> System.out.println(Arrays.stream(x).mapToInt(Integer::intValue).sum()),
        y -> System.out.println(Arrays.stream(y).mapToInt(Integer::intValue).average().getAsDouble()));
     }
     
     public void consumer(Integer[] fraction, Consumer<Integer[]> x, Consumer<Integer[]> y) {
      x.andThen(y).accept(fraction);
     }
     
    }

    输出结果:

    505
    84.16666666666667

    由于Consumerdefault方法所带来的嵌套调用(连锁调用),对行为的抽象的函数式编程理念,展示的淋漓尽致。

    其他的消费型函数式接口汇总说明:

    java.util.function.Supplier<T> 接口仅包含一个无参的方法: T get() ,用来获取一个泛型参数指定类型的对象数据。

    接口源码:

    package java.util.function;

    @FunctionalInterface
    public interface Supplier<T{
        get();
    }

    由于这是一个函数式接口,意味着对应的Lambda表达式需要对外提供一个符合泛型类型的对象数据。

    public class SupplierTest {

     public int getMax(Supplier<Integer> supplier) {
      return supplier.get();
     }
     
     /**
      * 获取数组元素最大值
      */

     @Test
     public void getMaxTest() {
      Integer[] data = new Integer[] { 546321 };
      int result = getMax(() -> {
       int max = 0;
       for (int i = 0; i < data.length; i++) {
        max = Math.max(max, data[i]);
       }
       return max;
      });
      System.out.println(result);
     }
     
    }

    其他的供给型函数式接口汇总说明:

    java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

    接口源码:

    package java.util.function;

    import java.util.Objects;

    @FunctionalInterface
    public interface Function<TR{

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

    来一个自驾九寨沟的代码示例:

    public class FunctionTest {
     
     @Test
     public void findByFunctionTest() {
      Function<BigDecimal, BigDecimal> getMoney = m -> m.add(new BigDecimal(1000));
      BigDecimal totalCost = getMoney.apply(new BigDecimal(500));
      System.out.println("张三的钱包原本只有500元,自驾川西得去银行再取1000元,取钱后张三钱包总共有" +           Function.identity().apply(totalCost) + "元");
      BigDecimal surplus = cost(totalCost, (m) -> {
       System.out.println("第二天出发前发现油不足,加油前有" + m + "元");
       BigDecimal lubricate = m.subtract(new BigDecimal(300));
       System.out.println("加油300后还剩余" + lubricate + "元");
       return lubricate;
      }, (m) -> {
       System.out.println("到达景区门口,买景区票前有" + m + "元");
       BigDecimal tickets = m.subtract(new BigDecimal(290));
       System.out.println("买景区票290后还剩余" + tickets + "元");
       return tickets;
      });
      System.out.println("最后张三返程到家还剩余" + surplus + "元");
     }

     public BigDecimal cost(BigDecimal money, Function<BigDecimal, BigDecimal> lubricateCost,
       Function<BigDecimal, BigDecimal> ticketsCost)
     
    {
      Function<BigDecimal, BigDecimal> firstNight = (m) -> {
       System.out.println("第一晚在成都住宿前有" + m + "元");
       BigDecimal first = m.subtract(new BigDecimal(200));
       System.out.println("交完200住宿费还剩余" + first + "元");
       return first;
      };
      Function<BigDecimal, BigDecimal> secondNight = (m) -> {
       System.out.println("第二晚在九寨县住宿前有" + m + "元");
       BigDecimal second = m.subtract(new BigDecimal(200));
       System.out.println("交完200住宿费还剩余" + second + "元");
       return second;
      };
      return lubricateCost.andThen(ticketsCost).andThen(secondNight).compose(firstNight).apply(money);
     }

    }

    输出结果:

    张三的钱包原本只有500元,自驾川西得去银行再取1000元,取钱后张三钱包总共有1500
    第一晚在成都住宿前有1500
    交完200住宿费还剩余1300
    第二天出发前发现油不足,加油前有1300
    加油300后还剩余1000
    到达景区门口,买景区票前有1000
    买景区票290后还剩余710
    第二晚在九寨县住宿前有710
    交完200住宿费还剩余510
    最后张三返程到家还剩余510

    其他的函数型函数式接口汇总说明:

    java.util.function.Predicate<T> 接口中包含一个抽象方法: boolean test(T t) ,用于条件判断的场景。默认方法:and or nagte (取反)。

    接口源码:

    package java.util.function;

    import java.util.Objects;

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

        static <T> Predicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    }

    既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用逻辑连接起来实现并且的效果时,类始于 Consumer接口 andThen()函数 其他三个雷同。

    public class PredicateTest {
     /**
      * 查找在渝北的Jack
      */

     @Test
     public void findByPredicateTest() {
      List<User> list = Lists.newArrayList(new User("Johnson""渝北"), new User("Tom""渝中"), new User("Jack""渝北"));
      getNameAndAddress(list, (x) -> x.getAddress().equals("渝北"), (x) -> x.getName().equals("Jack"));
     }
     
     public void getNameAndAddress(List<User> users, Predicate<User> name, Predicate<User> address) {
      users.stream().filter(user -> name.and(address).test(user)).forEach(user -> System.out.println(user.toString()));
     }
    }

    输出结果:

    User [name=Jack, address=渝北]

    其他的断言型函数式接口汇总说明:

    四、总结

    Lambda 表达式和方法引用并没有将 Java 转换成函数式语言,而是提供了对函数式编程的支持。这对 Java 来说是一个巨大的改进,因为这允许你编写更简洁明了,易于理解的代码。

    10-31 01:39