3

为什么我们不能像组合函数一样组合函数和消费者?

Function<Integer, String> a = Object::toString;
Consumer<String> b = x -> System.out.println(x);
Consumer<Integer> composed = a.andThen(b);

这似乎是对现有 Function 接口的明显即兴创作。在 Java 8 中避免这种能力有什么原因吗?

另外,如下使用 Function 的扩展实现是一种常见的做法吗?

interface FunctionImproved<T, R> extends Function<T, R> {
    default Consumer<T> andThen(Consumer<R> consumer) {
        return x -> consumer.accept(apply(x));
    }
}
4

4 回答 4

2

实例上的andThen方法需要 a ,而不是 a :Function<A0, A1>Function<A1, A2>Consumer<A1>

a.andThen(string -> {
    b.accept(string);
    return string;
});

它有一个副作用,因为我们的“消费者”(实际上是 a Function<A1, A1>)将从前一个Function<Integer, String> a函数返回一个被忽略的值。这不是我们想要的。


组合 aFunction<A0, A1>和 a有不同的方法Consumer<A1>

Consumer<Integer> c = i -> b.accept(a.apply(i));

可以推广到实用方法:

<A0, A1> Consumer<A0> functionAndThenConsumer(Function<A0, A1> f, Consumer<A1> c) {
    return i -> c.accept(f.apply(i));
}

使用 的扩展实现是一种常见的做法Function吗?

有两种选择:

  1. 拥有在功能接口之间进行一些转换的静态方法(就像我所做的那样)。

  2. 使用默认方法扩展这些标准功能接口(就像您所做的那样)。只需选择一个正确有意义的名称,而不是像FunctionImproved.

于 2017-11-27T21:36:55.650 回答
1

Functions 和Consumers 服务于两个不同的主人(在某种意义上)。

  • 一个函数接受一个(或在 的情况下是两个BiFunction)类型并产生第三种类型,它可以是第一种或第二种类型。
  • 消费者接受一种(或两种情况下BiConsumer)类型,并且产生输出。

这两种情况下,都应该执行一个离散单一的操作,这样可以更容易地推断操作的线程安全性。

看起来您正在尝试将一种类型简单地映射到另一种类型(字符串到整数),然后对其调用操作。如果您的元素在 a 中Stream,则可以写为:

elements.map(Objects::toString).forEach(System.out::println);

...如果它在一个集合中,或者IntStream,你可以使用它来代替:

elements.forEach(System.out::println);

最终,您尝试的可能是寻找问题的解决方案。一旦您更清楚地了解功能和消费者实际扮演的角色,尝试上述组合就变得不那么必要了。

于 2017-11-27T21:45:12.517 回答
1

为什么我们不能像组合函数一样组合函数和消费者?

这都是关于 Java 的类型系统的。该Function.andThen方法接受 type 的参数Function(不是 type Consumer)。ConsumerJava 中的 a和 a之间没有关系Function,即一个不扩展另一个,所以你不能将 a 传递ConsumerFunction'sandThen方法。

不过,有可用的解决方法。如您所示,一种是使用默认方法,而另一种方法是使用静态方法,如Andrew's answer所示。

这是另一种方式,使用高阶函数:

BiFunction<Function<T, R>, Consumer<R>, Consumer<T>> composer =
    (function, consumer) -> t -> consumer.accept(function.apply(t));

你可以这样使用它:

Consumer<Integer> composed = composer.apply(Integer::toString, System.out::println);
于 2017-11-27T22:30:38.297 回答
-1

没有理由,为什么这个组合不应该作为 Function API 的一部分存在。Java 8 的设计者根本没有预见到所有可能的组合和用例:这就是 Java 9 添加了更多便利方法的原因,可能我们会在未来的版本中看到更多。

关于“改进”的功能,我不敢仅仅为此创建一个新的接口,尤其是考虑到接口名称的选择。后缀“Improved”,就像所有“Utils”、“Helpers”等一样,不具备自我解释的语义,应该避免使用。更好的解决方案是创建一个这样的功能命名空间:

final class Compositions {
     private Compositions() {}
     public static <T,R> Function<Consumer<R>, Consumer<T>> to(Function<T,R> f) { 
          return consumer -> (value -> consumer.accept(f.apply(value));
     } 
}

import static Compositions.*;
Function<Integer,String> stringValue = Integer::toString;
Consumer<Integer> printInt = to(stringValue).apply(System.out::println);
printInt.accept(1);
于 2017-11-27T22:31:33.840 回答