Кэширование объектов Byte в штучной упаковке не требуется спецификацией Java 13 SE?

Читая спецификацию JAVA 13 SE, я нашел в главе 5 раздел 5.1.7. Boxing Conversion следующие гарантии:

Если заключенное в рамку значение p является результатом вычисления константного выражения (§15.28) типа boolean, char, short, int или long, и результатом является true, false, символ в диапазоне от '' до ' ' включительно или целое число в диапазоне от -128 до 127 включительно, тогда пусть a и b будут результатами любых двух преобразований p в боксе. Всегда так, что a == b

Мне кажется странным, что значения типа byte не включены в эту формулировку.

Например, в таком коде, как:

Byte b1=(byte)4;
Byte b2=(byte)4;
System.out.println(b1==b2);

У нас есть константное выражение типа byte, и после упаковки значения b1 и b2 могут быть или не быть одним и тем же объектом.

На самом деле это работает так же без приведения:

Byte b1=4;

Здесь у нас есть константное выражение типа int в контексте присваивания. Так, согласно спецификации

Сужающее примитивное преобразование, за которым следует упаковывающее преобразование, может использоваться, если переменная имеет тип Byte, Short или Character, а значение константного выражения может быть представлено в типе byte, short или char соответственно.

Таким образом, выражение будет преобразовано в байт, и это значение типа байта будет помещено в коробку, поэтому нет гарантии, что значение будет интернировано.

Мой вопрос в том, правильно ли я интерпретирую спецификацию, или я что-то упускаю? Я посмотрел, требует ли спецификация использования метода Byte.valueOf() для упаковки (для чего это было бы гарантировано), но это не так.


person lpetru    schedule 01.01.2020    source источник
comment
В спецификации языка не требуется использовать кэшированное значение.   -  person Andy Turner    schedule 01.01.2020
comment
Возможно, связано с тем, что вызывает ли автоупаковка valueOf()?, заявляя, что valueOf() не является обязательным.   -  person lpetru    schedule 02.01.2020


Ответы (2)


TL;DR это было исправлено с помощью JDK 14, который теперь включает byte.

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

Обратите внимание на текст аналога JLS 6. :

Если заключенное в рамку значение p равно true, false, byte, char в диапазоне от до  или int или short в диапазоне от -128 до 127, то пусть r1 и r2 — результат любых двух преобразований p. всегда имеет место, что r1 == р2.

Здесь byte явно упоминается как упакованный в объект с канонической идентичностью, безоговорочно. Поскольку все байты находятся в диапазоне -127..128, в добавлении такого ограничения не было необходимости.

Но обратите внимание, что long не упоминалось.

Затем ознакомьтесь с JDK-7190924, 5.1.7: JLS не упоминает кэширование автоматически упакованных лонгов< /а>

В комментариях можно посмотреть, как это было.

В своем первом комментарии Алекс Бакли критикует, что «байт — это тип, а не значение», не принимая во внимание, что «байт» может означать «все значения в диапазоне байтов», но поскольку он также предполагает, что «число» изначально означало «буквальный " (вместо, например, "числового значения"), он акцентирует внимание на том, что все целочисленные литералы имеют тип int или long.

В его первом наброске используется термин «целочисленный литерал» и полностью удаляются типы. Слегка измененная его версия вошла в состав Java 8 JLS:

Если упаковываемое значение p представляет собой целочисленный литерал типа int между -128 и 127 включительно (§3.10.1), или логический литерал true или false (§3.10.3), или символьный литерал между '\u0000' и '\u007f' включительно (§3.10.3). 3.10.4), тогда пусть a и b будут результатом любых двух преобразований p в боксе. Это всегда так, что a == b.

Так что в Java 8 тип вообще не имеет значения, но гарантия ограничена литералами.

Таким образом, это будет означать, что

Byte b1 = 4;

оценивается как канонический объект из-за целочисленного литерала, где как

Byte b1 = (byte)4;

не может, так как (byte)4 является константным выражением, а не литералом.

В своем следующем комментарии, годы спустя, он рассматривает «константные выражения», которые действительно могут быть типизированы, и переформулирует фразу, возвращая типы «boolean, char, short, int или long», добавляя long, но забывая про "байт".

Эта результирующая фраза - это то, что вы процитировали, которая находится в спецификации, начиная с Java 9.

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

Хотя ограничение кэширования константами времени компиляции, когда JLS 6 указал его для всех значений в диапазоне без такого ограничения, уже является критическим изменением (что на практике не имеет значения, пока оно реализовано через valueOf, у которого нет возможности узнать, произошло ли значение из константы времени компиляции или нет).

В качестве примечания документация Byte.valueOf(byte) прямо говорит:

... все значения байтов кэшируются

пока начиная с Java 7< /а>.

person Holger    schedule 08.01.2020
comment
Существует разница между документацией (конкретной реализации) и JLS. Вопрос касается JLS. - person tevemadar; 08.01.2020
comment
@tevemadar документация по API, размещенная Oracle, является спецификацией API. Других авторитетных спецификаций API нет. Вещи, которые являются деталями реализации, явно отмечены как таковые. Кроме того, я не вижу смысла сосредотачиваться на небольшом примечании моего ответа, когда весь ответ касается JLS, а не упомянутого в примечании API. Это особенно весело, поскольку ваш ответ цитирует комментарии к коду классов внутренней реализации. - person Holger; 08.01.2020
comment
Очень интересная история. - person Eugene; 08.01.2020
comment
Я не вижу смысла сосредотачиваться на небольшом примечании к моему ответу - так что вы можете злонамеренно, а также ошибочно придираться к части моего ответа, но я могу не указывать, когда вы говорите о что-то не относящееся к вопросу. - person tevemadar; 08.01.2020
comment
@tevemadar есть фундаментальная разница между неправильным утверждением и несвязанным утверждением. - person Holger; 09.01.2020
comment
Обновлено. JLS 14 включает byte, что указывает на непреднамеренное упущение в JLS 13. - person Holger; 26.05.2020

Вы правильно понимаете. Конец того же раздела 5.1.7 (от https://docs.oracle.com/javase/specs/jls/se13/html/jls-5.html) говорит:

Упаковочное преобразование может привести к ошибке OutOfMemoryError, если необходимо выделить новый экземпляр одного из классов-оболочек (Boolean, Byte, Character, Short, Integer, Long, Float или Double) и недостаточно места для хранения. доступен.

Byte не было бы там, если бы ожидалось, что он будет сгенерирован заранее.

И еще, еще из того же абзаца:

В идеале упаковка примитивного значения всегда будет давать идентичную ссылку. На практике это может оказаться неосуществимым при использовании существующих методов реализации. Приведенное выше правило является прагматичным компромиссом, требующим, чтобы определенные общие значения всегда помещались в неразличимые объекты. Реализация может кэшировать их, лениво или нетерпеливо. Для других значений правило запрещает программисту делать какие-либо предположения об идентичности заключенных в рамки значений. Это позволяет (но не требует) делиться некоторыми или всеми этими ссылками.


Не "доказательство", но, возможно, стоит упомянуть: Integer описывает бокс-обещание, 13 и даже 7

 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.

Текст тот же, хотя реализация менялась со временем.

Byte такого оператора нет, хотя он тоже кэшируется. 7, 13. Кэш есть в обоих, но про него ни слова (и про бокс тоже).

person tevemadar    schedule 01.01.2020
comment
То, что OutOfMemoryError может случиться, это что-то другое, я думаю. Также упоминается логическое значение, хотя требуется кэширование логических значений. Значение может быть создано лениво и только затем кэшировано. Ошибка OutOfMemoryError может возникнуть при создании первого значения при добавлении его в кеш. - person lpetru; 01.01.2020
comment
@lpetru добавил еще одну часть. Быть необходимым/ненужным и даже происходить нормально — это не то же самое, что быть гарантированным. - person tevemadar; 01.01.2020
comment
об этом нет ни слова, кроме своей документации, в которой говорится, что все байтовые значения кэшируются - person Holger; 08.01.2020
comment
@Holger, в Byte.java об этом нет ни слова, что это требование JLS, в то время как я нашел такое указание в Integer.java. Это то, что там написано, учитывая контекст, то есть JLS (из вопроса) и реализацию (то, что я связал). Я ничего не говорил о документации. И, на мой взгляд, в документации сказано, что это работает так, в то время как JLS говорит, что так и должно работать. - person tevemadar; 08.01.2020
comment
Ваше предложение: «Кэш есть в обоих, но о нем нет ни слова…», поэтому «это» относится к кешу, а не к JLS. Термин JLS ​​даже не встречается в двух предыдущих предложениях, поэтому я сомневаюсь, что кто-либо из читателей сделает вывод, что «это» относится к JLS. - person Holger; 08.01.2020
comment
@Holget ... так что даже не имеет значения, что это такое, что не упоминается, потому что ничего не упоминается. Byte.java просто не описывает свой кеш, а если это требование или это связано с боксом, то вообще. Пока Integer.java делает. - person tevemadar; 08.01.2020
comment
@Holger комментарий к документации метода API valueOf, несколько строк под той частью, на которую вы ссылаетесь, где говорится, что «все байтовые значения кэшируются» — это комментарий к документации, не относящийся к JLS и поэтому не имеет отношения к вопросу. На заметку: я, конечно, не буду касаться этого ответа, поскольку из-за примененного приятного тона я предпочитаю избегать даже того вида, который я когда-либо считал вашими комментариями актуальными. - person tevemadar; 09.01.2020
comment
Весь ваш последний раздел «не имеет отношения к вопросу». Вы даже начинаете это со слов «Не доказательство», и, как уже было сказано, этот комментарий, относящийся к JLS, присутствует только в Integer, он вообще не относится к Byte. Говоря о релевантности, первый вывод вашего ответа также неверен, поскольку утверждение о том, что упаковка может привести к OutOfMemoryError, касается упаковки в целом, не противореча возможности того, что упаковка некоторых констант времени компиляции приводит к предварительному выделенные объекты или лениво выделенные объекты - person Holger; 09.01.2020
comment
Как сказано, ни Boolean, ни Short, ни Character, ни Long не содержат такого комментария, относящегося к JLS, только Integer, поэтому «по сравнению» можно сделать вывод, что только Integer имеет такую ​​гарантию, даже когда текущий JLS явно говорит, что это логическое, короткое, символьное и длинное значение? Вопрос в том, является ли «byte» особенным, но комментарий, появляющийся только в Integer, сделает int особенным, так что это не имеет значения. В любом случае, нет необходимости снова комментировать, я согласен с тем, что у вас другое мнение, и продолжаю игнорировать вещи только потому, что это сказал я. - person Holger; 09.01.2020