If I am writing .NET code I would often expose IEnumerable
where-ever it could possibly make sense. Maybe it was down to LINQ and the fact you could us
Whether or not you want to be a Stream
yourself, primarily depends on whether you're truly a Stream
with the corresponding powers, i.e. support parallelization (not all Stream
s do, but it's one of the many benefits of Stream
s, and it's an important one). Normally I'd rather be a Collection
and let myself be Stream
ed by the corresponding API calls.
The getStuff()
method, returning Stream<String>
from myCollection.stream()
looks right to me.
Avoid Stream where possible in interfaces.
Streams may * be finite or infinite * be sequential or parallel * be ordered or non-ordered * need closing or not
Neither the implementors of an interface nor the clients of the interface can know which of these characteristics should (not) apply or which they need to guard against.
Choosing the most flexible return type in an interface just means that clients have the full burden of guarding against all possibilities. It is generally much more useful in an interface to restrict the returned datatype so that implementors know what assumptions they can rely one (for the same reason returning Collection
itself is not very useful in most cases, it is more useful to return List, Set, Iterable, InputStream
to indicate rights and duties of clients when consuming the data).
And when you need to change the characteristics of the returned data, change the interface,b break your clients intentionally, so that they can update their algorithms to the changed situation.
That's much more useful than to say: "My method returns a Stream, so now I can return an infinite stream instead of a finite one, and the client code still compiles, how wonderful!". It is not wonderful, it is a horrible source of bugs to do that. Client code should NOT continue to compile with such drastic changes, so using datatypes that are explicit about finity, parallelity, orderedness and duty to close()
is a much better choice.
Think like this: If remaining flexible was so great, then all interfaces that in Java7 return List
or Set
or Queue
would be returning Iterable
instead, to remain flexible in what datatype to actually return. But this is not what most interfaces do returning a Collection instead tells clients they can safely rely on a size() method, and returning a Set means they can safely rely on non-duplication, as an example. Being specific and restrictive in interfaces is not a sin, it is a virtue.
You are asking the wrong question. After all, it isn’t hard to support both, e.g.
Collection<Foo> getStuff();
default Stream<Foo> stuff() {
return getStuff().stream();
}
so code using your interface doesn’t need an explicit stream()
call, while implementors of the interface
don’t need to bother with it as well.
As you are always exposing a Stream
support whether via Collection.stream()
or explicitly, the question is whether you want to expose the Collection
. While it is cheap to provide a Stream
for a Collection
back-end it might turn out to be expensive to collect a Collection
from a Stream
.
So an interface exposing both ways suggests that they are equally usable while for an implementation not using a Collection
back-end one of these methods might be way more expensive than the other.
So if you are sure that all implementations, including future ones, will always use (or have to support) a Collection
it might be useful to expose it though the API as Collection
s support certain operations which Stream
doesn’t. That’s especially true if you support modification of the underlying data via the exposed Collection
.
Otherwise, supporting Stream
access only might be the better choice. This gives implementations the freedom to have other back-ends than a Collection
. However, that also implies that this API does not support Java versions prior to Java 8.