Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistency of primitive specializations in Java 8

Why does PrimitiveIterator.OfInt extend Iterator<Integer> but IntStream doesn't extend Stream<Integer>?

I'm trying to design a primitive collection type (similar to the interfaces from the Apache Commons Primitives library (http://commons.apache.org/dormant/commons-primitives/) and trying to be as consistent and compatible with the collections library, but I can't decide whether I should make my ByteList extend List<Byte> or not.

My guess is that this is because Iterator has direct syntax support in the language (i.e. for loops using iterators) so it's worth making the iterator compatible with that syntax even though it forces boxing, but I'm curious if anyone knows if there's a deeper reason. Thanks!

like image 604
rsalkeld Avatar asked Oct 30 '22 18:10

rsalkeld


1 Answers

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.

like image 173
Edwin Dalorzo Avatar answered Nov 10 '22 01:11

Edwin Dalorzo