2016-10-23 8 views
4

Почему PrimitiveIterator.OfInt простирается Iterator<Integer>, но IntStream не распространяется Stream<Integer>?Несогласованность примитивных специализаций в Java 8

Я пытаюсь создать примитивный тип коллекции (аналогично интерфейсам из библиотеки примитивов Apache Commons (http://commons.apache.org/dormant/commons-primitives/) и пытается быть последовательным и совместимым с библиотекой коллекций, но я не могу решить, должен ли я сделать мой ByteList продлить List<Byte> или нет.

Я думаю, что это потому, что Iterator имеет прямую поддержку синтаксиса в языке (т.е. для циклов с использованием итераторов), так что стоит сделать итератор совместимым с этим синтаксисом, даже если это заставляет бокс, но мне любопытно, если кто-нибудь знает, если есть более глубокая причина. Спасибо!

+0

Мое предположение заключается в том, что они хотели избежать загромождения интерфейса - чтобы расширить «поток», им пришлось бы хранить все методы, дублируя их с примитивной версией. Возможно, тот факт, что интерфейс для «Итератора» намного меньше (всего два метода, дублирующихся на примитивный тип), в этом случае чувствовал себя приемлемым для дизайнеров. – Hulk

+3

'PrimitiveIterator.OfInt' не имеет прямых конфликтов имен с' Iterator'; он добавляет только один метод 'nextInt', тогда как' IntStream' имеет всевозможные различные методы, особенно 'filter', которые не могут быть действительно перегружены с помощью' IntPredicate', а также 'Predicate '. –

+0

Примитивные версии 'Spliterator', соответствующие примитивным итераторам, например. [Spliterator.OfInt] (https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.OfInt.html), реализовать свой общий суперинтерфейс 'Spliterator ' a, предоставить как оболочку, так и примитив версии для своих методов. Но в любом случае у них есть только два метода, и перегрузка там прекрасна. – Hulk

ответ

0

Это редко можно было объяснить, почему API-интерфейсы JDK были разработаны так, как они это делали, но в этом случае мы можем легко увидеть, что попытка объединить API-интерфейсы Stream<Integer> и IntStream будет сложной задачей, так как существует ряд двусмысленностей в определения методов обоих интерфейсов.

Рассмотрим следующий пример:

interface Stream<T> { 
    Stream<T> distinct(); 
    Optional<T> findFirst(); 
} 

interface IntStream extends Stream<Integer> { 
    IntStream distinct(); 
    OptionalInt findFirst(); 
} 

Второй интерфейс не будет событием компиляции, так как подпись методов является то же самое, но возвращаемый тип отличается во втором интерфейсе.

Даже совместимые методы могут стать трудными в использовании, когда мы предоставляем несколько реализаций того же метода, которые принимают лямбда. Lambdas и перегрузка методов обычно плохо взаимодействуют друг с другом, потому что данная лямбда может реализовывать несколько функциональных интерфейсов. Например:

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); 
} 

Теперь, если у вас есть вызов, как stream.filter(n -> n > 10) это лямбда может реально реализовать оба Predicate<Integer> или IntPredicate и теперь пользователь API вынужден делать какое-то неоднозначность, например, (int n) -> n > 10, так как компилятор не может сказать разницы.

Я полагаю, что в долгосрочной перспективе это может помешать развитию API-интерфейсов Stream и IntStream.