interface BaseStream<T , S extends BaseStream<T , S>> 

I saw this construction in Java 8 and the brain went beyond the mind, how to understand this interface? How to properly implement it? When should such interfaces be introduced during development?

  • This is an interface that uses two generics ( Generics ). S must be derived from BaseStream . - Sanek Zhitnik
  • This interface is very similar to the implementation of the "decorator" programming pattern (see the code on the wiki) using generics. - MrFylypenko

2 answers 2

  public interface BaseStream<T , S extends BaseStream<T , S>> { abstract void doSomething(T t, S s); } 

one implementation

 public class BaseStreamImpl<S, I> implements BaseStream { public BaseStreamImpl(S s, BaseStream i ) { } @Override public void doSomething(Object o, BaseStream baseStream) { System.out.println(o); baseStream.doSomething("doSomething in BaseStreamImpl by " + baseStream.getClass().getName(), this); } } 

heir

 public class BaseStreamImplExtend<I ,S> extends BaseStreamImpl { public BaseStreamImplExtend(I i) { super(i, null ); System.out.println("This is BaseStreamImplExtend class2"); } @Override public void doSomething(Object o, BaseStream baseStream) { System.out.println(o); baseStream.doSomething("doSomething in BaseStreamImplExtend by " + baseStream.getClass().getName(), this); } } public static void main(String[] args) { BaseStreamImplExtend<Integer, String> baseStreamImplExtendInteger = new BaseStreamImplExtend(1); // благодаря дженерику ты говоришь какого типа будут аргументы конструктора. BaseStreamImplExtend<String, String> baseStreamImplExtendString = new BaseStreamImplExtend("String value"); // другой наследник принимает совершенно другое значение. если тут передать Integer, то компилятор сразу будет ругаться, а не при выполнении программы BaseStreamImpl bsI = new BaseStreamImpl("sd", baseStreamImplExtendInteger); BaseStreamImpl<String, Integer> bsImplSI = new BaseStreamImpl<>("BaseStreamImpl String value", baseStreamImplExtendInteger); // от того, что ты хочешь сделать зависит какой наследник передать для выполнения определенных задач bsI.doSomething("1234", baseStreamImplExtendInteger); } 

result

 This is BaseStreamImpl class 1 This is BaseStreamImplExtend class 1 This is BaseStreamImpl class String value This is BaseStreamImplExtend class String value This is BaseStreamImpl class sd This is BaseStreamImpl class BaseStreamImpl String value 1234 doSomething in BaseStreamImpl by BaseStreamImplExtend doSomething in BaseStreamImplExtend by BaseStreamImpl doSomething in BaseStreamImpl by BaseStreamImplExtend doSomething in BaseStreamImplExtend by BaseStreamImpl doSomething in BaseStreamImpl by BaseStreamImplExtend 

Very often, these kinds of interfaces are used when creating your own stream or filter.

It all depends on the tasks.

    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).