Почему появились новые java.утиль.Методы массивов в Java 8 не перегружены для всех примитивных типов?


Я просматриваю изменения API для Java 8, и я заметил, что новые методы в java.util.Arrays не перегружен для всех примитивов. Методы, которые я заметил:

в настоящее время эти новые методы обработки только int,long и double примитивы.

int,long и double вероятно, наиболее широко используемые примитивы, поэтому имеет смысл, что если бы им пришлось ограничить API, они выбрали бы эти три, но почему они должны были ограничить API?

1   51  

1 ответ:

чтобы решить вопросы в целом, а не только этот конкретный сценарий, я думаю, что мы все хотим знать....

почему есть загрязнение интерфейса в Java 8

например, в таком языке, как C#, существует набор предопределенных типов функций, принимающих любое количество аргументов с необязательным типом возврата ( Func и действие каждый из них собирается до 16 параметров различных типов T1,T2,T3, ...,T16), но в JDK 8 мы имеем набор различных функциональных интерфейсов, с разные имена и другой метод имена, и чьи абстрактные методы представляют собой подмножество известных арность функции (т. е. нулевой, унарный, двоичный, троичный и т. д.). И тогда у нас есть взрыв случаев, связанных с примитивными типами, и есть даже другие сценарии, вызывающие взрыв более функциональных интерфейсов.

Тип Стирания Выпуск

таким образом, в некотором смысле оба языка страдают от некоторой формы загрязнения интерфейса (или загрязнения делегата в C#). Единственное различие заключается в том, что в C# все они имеют одно и то же имя. В Java, к сожалению, из-за стирания типа нет никакой разницы между Function<T1,T2> и Function<T1,T2,T3> или Function<T1,T2,T3,...Tn>, так что, очевидно, мы не могли просто назвать их все одинаково, и нам пришлось придумать творческие имена для всех возможных типов функций сочетания.

не думаю, что экспертная группа не боролась с этой проблемой. По словам Брайана Гетца в список рассылки лямбда:

[...] В качестве единственного примера возьмем типы функций. Лямбда strawman, предлагаемый в devoxx, имел типы функций. Я настояла снять них, и это сделало меня непопулярным. Но мое возражение против типов функций не то чтобы мне не нравились типы функций -- я люблю типы функций -- но что типы функций плохо боролись с существующим аспектом Система типов Java, стирание. Стертые типы функций являются худшими из них эти два мира. Поэтому мы удалили это из дизайна.

но я не хочу говорить "Java никогда не будет иметь типы функций" (хотя я признаю, что Java может никогда не иметь типов функций.) Я поверьте, что для того, чтобы добраться до типов функций, мы должны сначала разобраться со стиранием. Это может быть, а может и не быть возможным. Но в мире овеществленная конструкция типы, типы функций начинают делать намного больше чувство.[ ..]

преимущество этого подхода заключается в том, что мы можем определить наши собственные типы интерфейса с методами, принимающими столько аргументов, сколько мы хотели бы, и мы могли бы использовать их для создания лямбда-выражений и ссылок на методы, как мы считаем нужным. Другими словами, у нас есть власть загрязнять мир С еще более новыми функциональными интерфейсами. Также мы можем создавать лямбда-выражения даже для интерфейсов в более ранние версии JDK или для более ранних версий наших собственных API, которые определяли такие типы SAM. И теперь у нас есть возможность использовать Runnable и Callable как функциональные интерфейсы.

однако эти интерфейсы становятся более трудными для запоминания, поскольку все они имеют разные имена и методы.

тем не менее, я один из тех, кто задается вопросом, почему они не решили проблему, как в Scala, определяя интерфейсы, такие как Function0,Function1,Function2, ...,FunctionN. Возможно, единственный аргумент, который я могу придумать против этого, заключается в том, что они хотели максимизировать возможности определения лямбда-выражений для интерфейсов в более ранних версиях API, как упоминалось ранее.

проблема отсутствия типов значений

таким образом, очевидно, стирание типа является одной из движущих сил здесь. Но если вы один из тех, кто задается вопросом, почему нам также нужны все эти дополнительные функциональные интерфейсы с похожими именами и сигнатурами методов и чье единственное отличие заключается в том, что использование примитивного типа, то позвольте мне напомнить вам, что в Java мы иотсутствие типов значений как и в языке, как C#. Это означает, что универсальные типы, используемые в наших универсальных классах, могут быть только ссылочными типами, а не примитивными типами.

другими словами, мы не можем сделать это:

List<int> numbers = asList(1,2,3,4,5);

но мы действительно можем сделать это:

List<Integer> numbers = asList(1,2,3,4,5);

второй пример, однако, берет на себя стоимость бокса и распаковки завернутого объекты назад и вперед от / до примитивных типов. Это может стать очень дорогим в операциях, связанных с коллекциями примитивных значений. Итак, экспертная группа решила создать это взрыв интерфейсов чтобы иметь дело с различными сценариями. Чтобы сделать вещи "менее хуже", они решили иметь дело только с тремя основными типами: int, long и double.

цитируя слова Брайана Гетца в список рассылки лямбда:

[...] В более общем плане: философия, лежащая в основе специализации примитивные потоки (например, IntStream) чреваты неприятными компромиссами. С одной стороны, это много уродливого дублирования кода, интерфейса загрязнение окружающей среды и т. д. С другой стороны, любая арифметика на коробочных операциях отстой, и не имея истории для сокращения более ints было бы ужасно. Так что мы находимся в трудном положении, и мы стараемся не делать хуже.

трюк №1 для того, чтобы не сделать его хуже: мы не делаем все восемь примитивный тип. Мы делаем int, long и double; все остальные может быть смоделирован ими. Возможно, мы могли бы избавиться от int тоже, но мы не думаем, что большинство разработчиков Java готовы к этому. Да, есть будут вызовы для персонажа, и ответ: "вставьте его в int." (Каждая специализация запроектирована к ~100K к следу ноги JRE.)

Трюк №2: мы используем примитивные потоки, чтобы выставить вещи, которые являются лучше всего сделать в примитивном домене (сортировка, сокращение) но не пытается дублировать все, что вы можете сделать в домене коробку. Например, нет IntStream.в (), как указывает Алексей. (Если бы были, следующий вопрос (ы) будет "где находится IntCollection? Интаррайлист? IntConcurrentSkipListMap?) Намерение состоит в том, что многие потоки могут начинаться как справочные потоки и в конечном итоге, как примитивные потоки, но не наоборот. Это нормально, и это уменьшает количество необходимых преобразований (например, нет перегрузка карты для int - > T, нет специализация функции для int - >T и др.) [...]

мы видим, что это было очень трудное решение для группы экспертов. Я думаю, что мало кто согласится, что это круто, и большинство из нас, скорее всего, согласится, что это было необходимо.

Проблема С Проверенными Исключениями

была третья движущая сила, которая могло бы быть еще хуже, и это тот факт, что Java поддерживает два типа исключений: checked и unchecked. Этот компилятор требует, чтобы мы обрабатывали или явно объявляли проверенные исключения, но он ничего не требует для непроверенных. Таким образом, это создает интересную проблему, потому что сигнатуры методов большинства функциональных интерфейсов не объявляют никаких исключений. Так, например, это невозможно:

Writer out = new StringWriter();
Consumer<String> printer = s -> out.write(s); //oops! compiler error

это невозможно сделать, потому что write операции выдает проверенное исключение (т. е. IOException), но подпись Consumer метод не объявляет его броски вообще никаких исключений. Таким образом, единственным решением этой проблемы было бы создать еще больше интерфейсов, некоторые объявляющие исключения, а некоторые нет (или придумать еще один механизм на уровне языка для прозрачность исключением. Опять же, "менее хуже" группа экспертов решила ничего не делать в этом случае.

по словам Брайана Гетца в список рассылки лямбда:

[...] Да, вам придется обеспечьте свои собственные исключительные Сэм. Зато лямбда-преобразование будет отлично работать с ними.

EG обсудил дополнительную поддержку языка и библиотеки для этого проблема, и в конце концов почувствовал, что это было плохо затраты / выгоды компромисс.

библиотечные решения вызывают взрыв 2x в типах SAM (исключительный vs not), которые плохо взаимодействуют с существующими комбинаторными взрывами для примитивной специализации.

доступные языковые решения были проигравшими от a компромисс сложности / ценности. Хотя есть некоторые альтернативы решения мы будем продолжать изучать, хотя явно не для 8 и, вероятно, не для 9 тоже.

в то же время, у вас есть инструменты, чтобы сделать то, что вы хотите. Я понимаю это вы предпочитаете, чтобы мы предоставили вам эту последнюю милю (и, во-вторых, ваш запрос-это действительно тонко завуалированный запрос на "почему бы вам просто не дать вверх по проверенным исключениям уже"), но я думаю, что текущее состояние позволяет ты выполняешь свою работу. [...]

Итак, это зависит от нас, разработчиков, чтобы создать еще больше взрывов интерфейса чтобы иметь дело с ними в каждом конкретном случае:

interface IOConsumer<T> {
   void accept(T t) throws IOException;
}

static<T> Consumer<T> exceptionWrappingBlock(IOConsumer<T> b) {
   return e -> {
    try { b.accept(e); }
    catch (Exception ex) { throw new RuntimeException(ex); }
   };
}

чтобы делать:

Writer out = new StringWriter();
Consumer<String> printer = exceptionWrappingBlock(s -> out.write(s));

вероятно, в будущем (возможно, JDK 9), Когда мы получим поддержка типов значений в Java и овеществление, мы сможем избавиться (или, по крайней мере, больше не нужно использовать больше) некоторые из этих нескольких интерфейсов.

таким образом, мы видим, что группа экспертов боролась с несколькими проблемами проектирования. Необходимость, требование или ограничение для сохранения обратной совместимости затрудняли работу, тогда у нас есть другие важные условия, такие как отсутствие типов значений, стирание типов и проверенные исключения. Если бы у Java был первый и не хватало двух других, дизайн JDK 8, вероятно, был бы другим. Итак, мы все должны понимать, что это было сложно проблемы с большим количеством компромиссов, и EG должен был где-то провести линию и принять решение.