Tu Tran
Tu Tran

Reputation: 101

Why they integrate stream API to collection framework in java 8

When learning about design patterns I heard that delegation is better than inheritance in most cases.

Thus I wonder why the java8 team made the decision to integrate Stream API into the existing Collections framework instead of using delegation (construct a Stream based on the given Collection)?

Especially by doing so, they have to introduce the new concept of Interface's default method implementation that in turn, blur out the semantic of Interfaces vs. Abstract classes?

Upvotes: 1

Views: 822

Answers (5)

nantitv
nantitv

Reputation: 3733

You could view in this way- If lambda expressions were introduced in back jdk1.2 then the way Collection api must have designed/implemented will be like the 'stream library' or in another words stream is enhanced collection. Most of the existing codes are using Collection APIs as the data source. Therefore no one is going to use stream if stream is not able to create from a Collection APIs. For that reason they have to modify the existing interface. 'default' methods avoided the breaking of all existing interface and allowed to enhance your code without any issues.

More over 'default methods' provided a way to enhance your interface in future also. Still there are huge differences between default methods in interface and abstract classes.

There are some methods in some interface's where implementation will be same in most of the inherited classes. In that case you can implement it as default method if possible. Once you start using default method you will love it :)

Last but not least "Language should always evolve; it should not stuck up on what you designed 20 years ago" :)

Upvotes: 0

davidxxx
davidxxx

Reputation: 131326

Why they integrate stream API to collection framework in java 8

Because before Java 8, Java collections features had a strong delay on concurrent languages such as C#, Scala, Ruby, etc... that provides out of the box and most of time in a conciser way a rich set of functional methods for collections but also pipeline processing for collections.

Thus I wonder why the java8 team made the decision to integrate Stream API into the existing Collections framework instead of using delegation (construct a Stream based on the given Collection)?

It would make the API of Stream less fluent, with more boiler plate code and as a consequence, it would not make intrinsically evolve the Java Collections features.

Imagine writing this code with a wrapper at each time :

List<String> strings = new ArrayList<>();
...
Streams.stream(strings).filter(....);

instead of :

List<String> strings = new ArrayList<>();
...
strings.stream().filter(....);

Imagine with a chained stream :

List<Integer> listOne = new ArrayList<>();
List<Integer> listTwo = new ArrayList<>();
...
List<int[]> values = Streams.stream(listOne)
    .flatMap(i -> Streams.stream(listTwo)
        .map(j -> new int[] { i, j }))
    .collect(Collectors.toList());

instead of

List<Integer> listOne = new ArrayList<>();
List<Integer> listTwo = new ArrayList<>();
...
List<int[]> values = listOne.stream()
    .flatMap(i -> listTwo.stream()
        .map(j -> new int[] { i, j }))
    .collect(Collectors.toList());

Especially by doing so, they have to introduce the new concept of Interface's default method implementation that in turn, blur out the semantic of Interfaces vs. Abstract classes?

It may be disconcerting at the beginning but finally it gives a powerful way to make evolve an API without breaking the client code using the old API.
Default methods should not be considered as a way to create abstract classes with common processings for all subclasses but a way to make evolve an API without breaking the compatibility with clients of older versions of the API.

Extract of the documentation on default methods :

Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.

Upvotes: 1

Holger
Holger

Reputation: 298103

First of all, the support for default and static methods in interfaces was not added only to support the stream() method in the Collection interface.

It is a natural desire to provide utility methods and useful defaults when defining interface, leading to the pattern of having two different classes to host them, the interface and an associated utility class. In the case of useful defaults, implementors were still required to write delegation method to use them. Of course, this would even become worse with functional interfaces restricted to a single method. See also this answer.

So when you consider the language feature of default methods as already existing, the choice of using it in the Collection API for convenience is not so big. It still is delegation, just with a convenient entry point. It’s not different to, e.g. String.format(…) delegating to the java.util.Formatter facility or String.replaceAll delegating to java.util.regex.

But the Stream API can’t run on its own without any active support from the Collection API side. The minimum it would require, is an Iterator from the Collection. The Iterator has been superseded by Spliterator for the Stream API, but whether a Collection provides an Iterator or a Spliterator is not a fundamental design change. It’s something that lies in the collection’s responsibility. There is a default method creating a Spliterator from an Iterator to allow every existing collection to work with the new framework out-of-the-box, but each Collection implementation has the opportunity to override that method, providing a better suited, potentially more efficient Spliterator implementation. Most of the JRE’s standard collection implementations and nowadays a lot the 3rd party collections use that opportunity.

Being overridable is a property of the default methods that you can’t emulate with static methods in another class or package. So it actually works the other way round. E.g. you can still invoke the old Collections.sort methods, but they will delegate to the new List.sort default method, which can be overridden with a more efficient implementation.

Like you normally don’t deal with an Iterator manually when using for(Variable v: collection) …, you don’t deal with a Spliterator manually, when using collection.stream(). … .

Upvotes: 4

GhostCat
GhostCat

Reputation: 140407

Providing a framework that is intended to be used by millions of users is always about balancing ease of use and following strict guidelines.

And the very first point to understand: favor composition over inheritance is a good practice. But that doesn't mean that always prefer composition.

The new streams architecture is intended as a new core building block of the Java APIs. In that sense: it is a natural choice to allow turning collections into streams via a member function.

Beyond that, it seems that the people behind the java language favor fluent interfaces. In that sense they probably prefer

list.stream(). stream operations 

over

Streams.stream(list). stream operations

And of course: when you think about the one suggestion I put up here how to alternatively implement a conversion from list to stream - that would only work for the known collections classes. So another concept would be required, like

YourSpecialStreamCreator.(yourSpecialCollection).stream()

whereas the fact that the interface based stream()method allows you to simply @Override the default implementation when your special collection implementation has a need to do so. But you can still use all the other things around streams in that interface!

Beyond that: the idea of default methods in interfaces actually helps with many problems. Yes, they were needed to add those new functionality to interface - but heck: adding new methods to interfaces is something that most people would like to do at some point. So having a clear, defined way backed into the core language is just extremely helpful.

My personal two cent here: adding V2, V3, ... interfaces like:

interface MyFunctionality { ...

interface MyFunctionalityV2 extends MyFunctionality { 
  void someNewThing();

is just ugly and painful.

Upvotes: 3

holi-java
holi-java

Reputation: 30676

delegation is better than inheritance

I think you have wrote some wrong in your question. Actually, the correct form is Composition over Inheritance.

Indeed, Collection#stream & Collection#spliterator is designed for applying Factory Method pattern. which means subclasses can provided it own Stream/Spliteartor instance to enable features & promote the performance purpose in java.

IF there is no such factory methods in Collection, you must back to procedure code and check the actually type to create appropriate Streams in runtime.

You only see the default methods declared on Collection, have you saw the override methods on sub-classes, for example:

Collections#nCopies uses a CopiesList as below to create a Stream<E> by IntStream to promote the performance.

public Stream<E> stream() {
   return IntStream.range(0, n).mapToObj(i -> element);
}

ArrayList#spliterator uses a ArrayListSpliterator as below to create a fail-fast spliterator:

public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}

Upvotes: 4

Related Questions