• 注册
  • 后端开发博客 后端开发博客 关注:0 内容:702

    怎么样能把函数式接口用好|Java 开发实战

  • 查看作者
  • 打赏作者
  • 当前位置: 职业司 > 后端开发 > 后端开发博客 > 正文
    • 后端开发博客
    • 本文正在参加「Java主题月 - Java 开发实战」,详情查看 活动链接

      开篇

      这是我参与更文挑战的第3天,活动详情查看: 更文挑战

      JDK8大家知道并使用的特性莫过于Lambda,这个很多人会用,比如Stream流,但都是简单浅显的使用,比如调用集合的Stream API等,但不会自己定义函数接口或API,今天通过几个案例来提高下对Java 中函数式编程的使用。

      案例演示

      函数接口说明

      接口 输入参数 返回类型 说明
      UnaryOperator T T 一元函数,输入输出类型相同
      Predicate T boolean 断言
      Consumer T / 消费一个数据,只有输入没有输出
      Function<T,R> T R 输入 T 返回 R,有输入也有输出
      Supplier / T 提供一个数据,没有输入只有输出
      BiFunction<T,U,R> (T,U) R 两个输入参数
      BiPredicate<L, R> (L,R) boolean 两个输入参数
      BiConsumer<T, U> (T,U) void 两个输入参数
      BinaryOperator (T,T) T 二元函数,输入输出类型相同

      案例一

      Function<Integer, Integer> times2 = e -> e * 2;
      Function<Integer, Integer> squared = e -> e * e;
      // 先执行参数,再执行调用者
      /*
      * 1. 4 * 4 = 16 16 * 2 = 32
      */
      System.out.println("result: " + times2.compose(squared).apply(4)); // 32
      /*
      * 先执行调用者: 4 * 2 = 8 再执行then传入的function 8 * 8 = 64
      */
      System.out.println("result: " + times2.andThen(squared).apply(4)); // 64
      复制代码

      这两个方法,andThen表示在外面那个Function执行之后调用,compose表示在外面那个Function执行之前调用。
      怎么样能把函数式接口用好|Java 开发实战

      案例二

      @Test
      public void test2(){
      // 3个数相加
      Function<Integer, Function<Integer, IntFunction<Integer>>> addfun3 = x -> y -> z -> x + y + z ;
      // 7个数相加
      Function<Integer,
      Function<Integer,
      Function<Integer,
      Function<Integer,
      Function<Integer,
      Function<Integer, IntFunction<Integer>>>>>>>
      addfun7 = x -> y -> z -> a -> c -> b -> d -> x + y + z + a + c + b + d;
      // 1+2+3
      Integer sum = addfun3.apply(1).apply(2).apply(3);
      System.out.println(sum);
      }
      复制代码

      通过这种方式,可以实现链式编程的累加效果
      怎么样能把函数式接口用好|Java 开发实战

      案例三

      
      @SafeVarargs
      private static <R> Function<R, R> combineFunctions(Function<R, R>... functions) {
      return Arrays.stream(functions)
      .reduce(Function::andThen)
      .orElseThrow(() -> new IllegalArgumentException("No functions to combine"));
      }
      @Test
      public void test3() {
      Function<Integer, Integer> addfun2 = x -> x * x;
      final Integer apply = combineFunctions(addfun2, addfun2).apply(2);
      System.out.println(apply);
      String str = "1,2,3,4,5,6";
      Function<Object, Object> splitByComma = s -> ((String) s).split(",");
      Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new);
      Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get();
      Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str);
      System.out.println(max);
      }
      复制代码

      如果你想有一个接受可变长度的Function参数的方法,可以使用...描述符,其中泛型<R,R>代表输入与输入类型,使用泛型是为了兼容性更高

      怎么样能把函数式接口用好|Java 开发实战

      案例四

      @Test
      public void test4(){
      Function<Integer, Integer> addfun2 = x -> x * x;
      final Calculator<Integer, Integer> calculator = new Calculator<>(2);
      final Integer integer = calculator.combineFunctions(addfun2, addfun2);
      System.out.println(integer);
      }
      public class Calculator<R,T> {
      // 被操作的属性
      private Object input;
      public Calculator(Object input) {
      this.input = input;
      }
      // 可以通过这种方式定义对象自身的行为
      @SuppressWarnings("unchecked")
      @SafeVarargs
      public final R combineFunctions(Function<T, T>... functions) {
      return (R) Arrays.stream(functions)
      .reduce(Function::andThen)
      .orElseThrow(() -> new IllegalArgumentException("No functions to combine"))
      .apply((T)input);
      }
      }
      复制代码

      领域开发模式或多或少都听过、了解过,对象除了有自身的属性之外还可以有自身的行为,针对对象的行为方法也是可以使用函数式编程范式来定义的。

      怎么样能把函数式接口用好|Java 开发实战

      案例五

      // BiConsumer<T, Integer> 两个输入参数T、Integer
      public static <T> Consumer<T> consumerWithIndex(BiConsumer<T, Integer> consumer) {
      class Obj {
      int i;
      }
      // 只会被调用一次,原因看java.lang.Iterable#forEach
      Obj obj = new Obj();
      // 返回的Consumer函数
      return t -> {
      int index = obj.i++;
      // 这里执行System.out.println("list[" + index + "]=" + item),消费指定泛型的数据。
      consumer.accept(t, index);
      };
      }
      @Test
      public void test5(){
      val list = Arrays.asList("Hi", "I", "am", "Henry.Yao");
      // 2个元素为一组
      val partition = Lists.partition(list, 2);
      partition.forEach(LambdaUtils.consumerWithIndex((item, index) -> {
      System.out.println("list[" + index + "]=" + item);
      }));
      }
      复制代码

      Java8的forEach()循环对象的时候,是没有办法得到对象索引下标的,针对这个情况,可以声明一个函数式方法来做到,最终书写形式类似Scala、js的forEach语法,很有用!

      怎么样能把函数式接口用好|Java 开发实战

      java.lang.Iterable#forEach

      // action:是一个Consumer函数
      default void forEach(Consumer<? super T> action) {
      Objects.requireNonNull(action);
      for (T t : this) {
      /* 这里会循环调用Consumer函数,而consumerWithIndex返回的Consumer函数内容为
      t -> {
      int index = obj.i++;
      consumer.accept(t, index);
      }
      所以Obj obj = new Obj()只会被调用一次,这样就不用担心new Obj 时把 i 没有重置为0,不会发生的
      */
      consumer.accept(t, index);
      }
      action.accept(t);
      }
      }
      复制代码

      以上只是扩展了思路,更多有趣有用的写法用法,还需要大家去挖掘去创造,如果你也有新鲜有意思的函数式方法或工具类,欢迎评论区留言分享,我们一起进步。

      关注+点赞👍收藏❤️不迷路

      文章每周持续更新,可以微信搜索「 十分钟学编程 」第一时间阅读和催更,如果这个文章写得还不错,觉得有点东西的话
      各位的支持和认可,就是我创作的最大动力,我们下篇文章见!

      请登录之后再进行评论

      登录

      手机阅读天地(APP)

      • 微信公众号
      • 微信小程序
      • 安卓APP
      手机浏览,惊喜多多
      匿名树洞,说我想说!
      问答悬赏,VIP可见!
      密码可见,回复可见!
      即时聊天、群聊互动!
      宠物孵化,赠送礼物!
      动态像框,专属头衔!
      挑战/抽奖,金币送不停!
      赶紧体会下,不会让你失望!
    • 实时动态
    • 签到
    • 做任务
    • 发表内容
    • 偏好设置
    • 到底部
    • 帖子间隔 侧栏位置:
    • 还没有账号?点这里立即注册