使用 compose
或 andThen
的问题在于它们都内置于 Function
接口和类型中,无论是编译时类型还是运行时类型,它们返回的函数类型都是 Function
而不是 UnaryOperator
或你定义的任何子接口。例如,假设我们有以下代码:
UnaryOperator<String> a = s -> s + "bar";
UnaryOperator<String> b = s -> s + s;
有人可能会认为我们可以写
UnaryOperator<String> c = a.compose(b)
但是这不起作用!相反,我们必须写成
Function<String, String> c = a.compose(b)
为了使这个工作正常,UnaryOperator
必须提供andThen
和compose
的协变覆盖。 (可以说这是API中的一个错误)。您可以在子接口中执行相同的操作,或者手动编写lambda表达式也很简单。例如:
interface MyOperator extends UnaryOperator<String> { }
public static void main(String[] args) {
List<MyOperator> list =
Arrays.asList(s -> s + "bar",
s -> "[" + s + "]",
s -> s + s);
MyOperator composite =
list.stream()
.reduce(s -> s, (a, b) -> s -> b.apply(a.apply(s)));
System.out.println(composite.apply("foo"));
}
这会打印出[foobar] [foobar]
。请注意,我使用了reduce
的双参数形式,以避免处理Optional
。
或者,如果您经常进行函数组合,可以在自己的接口中重新实现所需的方法。这并不太难。这些基于java.util.Function
中的实现,但将本示例中使用的具体String
类型替换为泛型。
interface MyOperator extends UnaryOperator<String> {
static MyOperator identity() {
return s -> s;
}
default MyOperator andThen(MyOperator after) {
Objects.requireNonNull(after);
return s -> after.apply(this.apply(s));
}
default MyOperator compose(MyOperator before) {
Objects.requireNonNull(before);
return s -> this.apply(before.apply(s));
}
}
这将用于以下方面:
MyOperator composite =
list.stream()
.reduce(MyOperator.identity(), (a, b) -> a.andThen(b));
增加接口的复杂性,以便使用andThen
代替嵌套的lambda表达式,可能只是个人偏好问题。