传递行为和传递对象
在Spliterator中,终于遇到了一个在固有的java面向对象编程的思想中很难理解的一件事情,那就是传递对象和传递行为。代码如下
@Override
default boolean tryAdvance(Consumer<? super Integer> action) {
if (action instanceof IntConsumer) {
return tryAdvance((IntConsumer) action);
}
else {
if (Tripwire.ENABLED)
Tripwire.trip(getClass(),
"{0} calling Spliterator.OfInt.tryAdvance((IntConsumer) action::accept)");
return tryAdvance((IntConsumer) action::accept);
}
}
这是一段在Spliterator中具象化为特例Int类型的Spliterator,这一段代码中,我们可以看到,这里面的IntConsumer和Consumer两个类型,他们并没有关联关系,即非继承,
那么,他们为什么能实现强制类型转换呢?正常来讲这里的编译会出现错误,但是这就是在Java8中新增加的函数式编程所带来的效果,即–》传递行为,也就是说
Stream()
调用了Collection.stream()方法,
这里的ReferencePipeline.head实现了ReferencePipeline而他们就是Stream。
ReferencePipeline表示流的源stage和中间stage,
而ReferencePipeline.Head实现了对源数据的源stage的操作。
head是没有previousStage的,而ReferencePipeline有。
他们都有一个泛型<E_IN,E_OUT>,代表上游管道进入element和产出element。
For sequential streams, and parallel streams without stateful intermediate
operation, parallel streams, pipeline evaluation is done in a single pass that
“jams” all the operations together.
这是指流管道会将所有操作一起执行。
Collection.stream()其实就是构造一个spliterator来构造一个源及头。
而这里这个spliterator是被各种集合子类Overwrite了,所以,当调用foreach方法时,
foreach方法在源直接调用时调用的是head中的foreach,否则调用的是ReferencePipeline中的foreach。
调用的其实是foreachRemaining,而这个是各个子类实现中的foreachRemaining。
我们来看一下构建的头操作和中间操作。
这是头节点。调用了super(source,sourceFlags,parallel)也就是AbstractPipeline
中间节点都一样,我们以map为例。
我们可以看到map构造了一个StatelessOp对象,这个对象也是ReferencePipeline的一个子类实现。同样的也调用了super(source,sourceFlag)也就是AbstractPipeline。
然后我们来看这个AbstractPipeline。
上面的构造头,下面的构造中间类型,但是他们返回的都是AbstractPipeline。
他们的不同点一目了然,比如深度,比如是否有perviousstage等等。
而从上上图中,我们可以看到每一个中间操作其实都是创建了一个StatelessOp这样一个对象。这个对象里面重写了opWrapSink方法,然后创建了新的ChainedReference对象。
这个链式引用对象就关联了上下游的关系。
对于filter,是通过谓词来判断是否需要继续执行,如下
而对于map我们则可以理解为一种装饰者模式。对于每一个元素来说,都是先进行调用上游的Function再去执行下游的accept,是以元素作为执行链的基本单元。而不是执行完了整个管道的Function再去执行整个管道的Filter
而Sink中相比他的父类Consumer增加了模版模式的方法begin和end,形成了一个类似双向链表的调用链。
ChainedReference就对Sink进行了实现。来实现了上文我所说到的调用链关联上下游的一种功能。