It is seldom possible to tell why the JDK APIs were designed the way they did it, but in this case we can easily see that trying to make the APIs of Stream<Integer>
and IntStream
work together would be hard to do because there are a number of ambiguities in the method definitions of both interfaces.
Consider the following:
interface Stream<T> {
Stream<T> distinct();
Optional<T> findFirst();
}
interface IntStream extends Stream<Integer> {
IntStream distinct();
OptionalInt findFirst();
}
The second interface would not event compile since the signature of the methods is the same, but the return type is different in the second interface.
Even compatible methods may become difficult to use when we provide multiple implementations of the same method that accept lambdas. Lambdas and overloading of methods typically do not play well together because a given lambda may implement multiple functional interfaces. For example:
interface Stream<T> {
Stream<T> filter(Predicate<T> p);
<S> Stream<S> map(Function<T,S> mapper);
}
interface IntStream extends Stream<Integer> {
IntStream filter(IntPredicate p);
IntStream map(IntUnaryOperator mapper);
}
Now, if you have a invocation like stream.filter(n -> n > 10)
this lambda can actually implement both Predicate<Integer>
or IntPredicate
and now the API user is forced to do some sort of disambiguation, e.g. (int n) -> n > 10
, since the compiler cannot tell the difference.
I suppose, in the long term, this may hinder the evolution of the Stream
and IntStream
APIs.