减少Java 8中的UnaryOperators列表 [英] Reducing a list of UnaryOperators in Java 8
问题描述
在Java 8中减少一个UnaryOperator列表直到它们代表一个我可以调用apply的UnaryOperator的首选方法是什么? 例如,我有以下
What is the preferred way of reducing a list of UnaryOperators in Java 8 till they represent one UnaryOperator that I can call apply on? For example I have the following
interface MyFilter extends UnaryOperator<MyObject>{};
public MyObject filterIt(List<MyFilter> filters,MyObject obj){
Optional<MyFilter> mf = filters
.stream()
.reduce( (f1,f2)->(MyFilter)f1.andThen(f2));
return mf.map(f->f.apply(obj)).orElse(obj);
}
但是此代码在(MyFilter)f1.andThen(f2)
处抛出ClassCastException
.
我真的很想要这段代码的效果:
But this code throws a ClassCastException
at (MyFilter)f1.andThen(f2)
.
I really want the effect of this code in the end:
MyObject o = obj;
for(MyFilter f:filters){
o = f.apply(o);
}
return o;
但是我也很好奇我们如何使用compose
或andThen
将一组函数简化为一个函数.
But I am also curious of how we can reduce a collection of functions to one function, using compose
or andThen
.
推荐答案
使用compose
或andThen
的问题在于它们是内置在Function
接口和类型中的-编译时和编译时运行时类型-它们返回的函数是Function
而不是UnaryOperator
或您定义的子接口.例如,假设我们有
The problem with using compose
or andThen
is that they're built into the Function
interface and the type -- both compile-time and runtime types -- of the functions they return is Function
and not UnaryOperator
or a subinterface such as you've defined. For example, suppose we have
UnaryOperator<String> a = s -> s + "bar";
UnaryOperator<String> b = s -> s + s;
一个人可能以为我们可以写
One might think we could write
UnaryOperator<String> c = a.compose(b);
但这不起作用!相反,必须写
but this doesn't work! Instead, one has to write
Function<String, String> c = a.compose(b);
为此,UnaryOperator
必须提供andThen
和compose
的协变量替代. (可以说这是API中的错误.)您可以在子接口中执行相同的操作.或者,手动写出lambda很简单.例如,
For this to work, UnaryOperator
would have to provide covariant overrides of andThen
and compose
. (Arguably this is a bug in the API.) You'd do the same in your subinterface. Or, it's simple enough to write out the lambdas by hand. For example,
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]
.请注意,为了避免不得不处理Optional
,我使用了reduce
的两个参数形式.
This prints out [foobar][foobar]
. Note that I've used the two-arg form of reduce
in order to avoid having to deal with Optional
.
或者,如果您经常进行函数组合,则可以在自己的界面中重新实现所需的方法.不太难.这些基于java.util.Function
中的实现,但是在本示例中我一直使用具体的String
类型代替泛型.
Alternatively, if you're doing function composition a lot, you could reimplement the methods you need in your own interface. It's not too hard. These are based on the implementations in java.util.Function
but with the concrete String
type I've been using in this example substituted for the generics.
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只是一个问题.
Whether bulking up the interface in order to write andThen
instead of a nested lambda is a matter of taste, I guess.
这篇关于减少Java 8中的UnaryOperators列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!