Such a design is needed to implement the "generating" patterns while maintaining encapsulation. For example, the BaseStream interface has a method
S parallel()
Which will return the "parallel" stream equivalent to yours.
Suppose you have a stream MyStream for objects of type MyObject .
class MyStream implements BaseStream<MyObject, MyStream >
And there is also a stream that can work with multiple threads -
class MyParallelStream extends MyStream { public MyParallelStream(MyStream parent) { //Инициализация параллельного стрима данными } }
In this case, you can implement the method in MyStream
public MyStream parallel() { return new MyParallelStream(this); }
And from the outside it will look like
MyStream oldStream = new MyStream();//конструктор как пример, на самом деле - некоторая инициализация. MyStream newParallel = oldStream.parallel();
And you are already working with a fast parallel version without even thinking that something has changed in your classes.
Another advantage of this approach is that if you find that the MyParallelStream implementation contains errors that are very difficult and long to fix, you can release a quick fix by correcting the method
public MyStream parallel() { return this; }
Now the method returns your old proven stream, which, although it will not process elements in a multi-threaded manner, will at least work correctly (albeit more slowly than expected).
Smust be derived fromBaseStream. - Sanek Zhitnik