【问题标题】:Composing a Java Function and Consumer组合 Java 函数和使用者
【发布时间】:2015-09-18 07:28:11
【问题描述】:

在功能上组合 java FunctionConsumer 的最佳方法是什么? 例如,给定一些Function<Object, String> f 和一些Consumer<String> c,然后执行f.andThen(c) 会感觉很自然,但这不是接口的工作方式。

我看到的两个选项是将Consumer<String> c 替换为Function<String, Void> c 或将Consumer<String> c 更改为BiConsumer<Function<Object, String>, String> c 并执行

accept(Function<Object, String> f, Object o) {
    String thing = f.apply(o);
    //do something with thing
}

其中一个比另一个更好吗?有没有更好的办法?

【问题讨论】:

  • 你能提供一个用例吗?
  • 我会为此编写自己的Consumer 类。大约需要 10 行代码。
  • @Tunaki 一个简单的用例是,如果我们有一个对象,我们想以不同的方式(函数)序列化为字符串,然后我们想将它输出到数据库或终端之类的地方(消费者)
  • @MarkoTopolnik 那会是什么样子?
  • 好吧,我错了 --- Misha 展示了它是如何在单行中完成的 :) 我想象的课程是手动实现的。

标签: java functional-programming java-8 function-composition


【解决方案1】:

这是你想要做的吗?

Consumer<Object> composed = o -> c.accept(f.apply(o));

如果您发现自己经常遇到这个问题,您可以创建一个静态方法来执行此操作(但它真的值得吗?):

static<T,R> Consumer<T> applyAndAccept(Function<? super T,? extends R> f, Consumer<R> c){
    return t -> c.accept(f.apply(t));
}

【讨论】:

  • 有时值得:您可以使用两个很酷的方法引用而不是单个无聊的 lambda。如果您的方法引用是实例绑定的并且实例存储在非最终变量中,那么 lambda 版本可能看起来非常笨拙。在这种情况下,您提出的静态方法甚至比不存在的默认方法 andThen(Consumer) 更好,因为将自动推断方法引用类型。
  • @TagirValeev 我同意,使用方法引用的能力是一大优势。我只是没有经常遇到Function-Consumer 的情况,但也许其他人会遇到。我一直碰到的是需要编写FunctionPredicate
  • @Misha 你为什么不经常遇到这种情况?你不经常这样做吗:arr.forEach(func1.andThen(func2).andThen(func3).andThen(consumer))
【解决方案2】:

您可以在自己的代码中复制 java.util.function.Function 接口。将其称为 ConsumableFunction 并将所有默认方法参数类型和返回值从 Function 更改为 ConsumableFunction。然后添加以下默认函数:

default Consumer<T> atLast(Consumer<R> consumer) {
    Objects.requireNonNull(consumer);
    return (T t) -> consumer.accept(apply(t));
}

现在让您的所有 Function 实现都实现 ConsumableFunction。然后你可以这样写代码:

Consumer<A> consumerForA = functionFromAToB
    .andThen(functionFromBToC)
    .andThen(functionFromCToD)
    .atLast(consumerForD);

【讨论】:

    【解决方案3】:

    这不是使用mapforEach 完成的吗?

    public void test() {
        Function<Object, String> f = (Object t) -> t.toString();
        Consumer<String> c = (String t) -> {
            System.out.println(t);
        };
        List<Object> data = new ArrayList<>();
        data.stream().map(f).forEach(c);
    }
    

    【讨论】:

    • 您基本上不执行任何操作,因为您的流缺少终端操作。流处理也只是使用Consumer 的一个例子。它们在其他地方也很有用。
    • @TagirValeev - 有趣 - 添加 .toArray() 似乎可以解决这个问题。
    • 这是对peek的滥用。如果c 不是线程安全的,只需执行.map(f).forEach(c).map(f).forEachOrdered(c)
    猜你喜欢
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-03
    • 2020-03-26
    相关资源
    最近更新 更多