Почему результирующий тип разделения коротких целых чисел в Java не является коротким целым числом?

Рассмотрим этот код:

public class ShortDivision {
    public static void main(String[] args) {
        short i = 2;
        short j = 1;
        short k = i/j;
    }
}

При компиляции это приводит к ошибке

ShortDivision.java:5: possible loss of precision
found   : int
required: short
        short k = i/j;

потому что тип выражения i/j, по-видимому, является int и поэтому должен быть отброшен до короткого.

Почему тип i/j не короткий?

Ответ 1

Из Java spec:

5.6.2 Двоичное числовое продвижение

Когда оператор применяет двоичное числовое продвижение к паре операндов, каждое из которых должно обозначать значение числового типа, применяются следующие правила, чтобы, используя расширяющееся преобразование (§5.1.2), преобразовывать операнды по мере необходимости

Если один из операндов имеет тип double, другой преобразуется в double.

В противном случае, если любой операнд имеет тип float, другой преобразуется в float.

В противном случае, если один из операндов имеет тип long, другой преобразуется в long.

В противном случае оба операнда преобразуются в тип int.

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


EDIT: Почему так? Короткий ответ заключается в том, что Java скопировала это поведение с C. Более длинный ответ, возможно, связан с тем, что все современные машины выполняют по крайней мере 32-разрядные собственные вычисления, и на некоторых машинах может быть сложнее делать 8-битные и 16-разрядные операции.

См. также: OR-ing байтов в С# дает int

Ответ 2

Что касается мотивации: представьте себе альтернативы этому поведению и посмотрите, почему они не работают:

Альтернатива 1: результат всегда должен быть таким же, как и входы.

Каким должен быть результат для добавления int и короткого?

Каким должен быть результат для умножения двух шорт? Результат в целом будет вписываться в int, но поскольку мы усекаем до краткости, большинство умножений будут терпеть неудачу. Кастинг в int впоследствии не поможет.

Альтернатива 2: результат всегда должен быть наименьшим типом, который может представлять все возможные выходы.

Если тип возврата был коротким, ответ не всегда был бы представлен как короткий.

Короткие могут содержать значения от 32 768 до 32 767. Тогда этот результат вызовет переполнение:

short result = -32768 / -1; // 32768: not a short

Итак, ваш вопрос: почему добавление двух ints не возвращает long? Что должно быть умножение двух ints? Вдоль? BigNumber, чтобы покрыть случай квадратичного значения минимального значения?

Альтернатива 3: выберите то, что большинство людей, вероятно, хотят большую часть времени

Таким образом, результат должен быть:

  • int для умножения двух коротких замыканий или любых операций int.
  • short при добавлении или вычитании шорт, деление короткого на любой целочисленный тип, умножение двух байтов,...
  • byte, если битвертирует байт вправо, int, если бит сдвигается влево.
  • и т.д...

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

Ответ 3

Это просто выбор дизайна, совместимый с C/С++, который доминировал над языками при разработке Java.

Например, я * j может быть реализован, поэтому тип продвигается с байта = > short, short = > int и int = > long, и это позволит избежать переполнения, но это не так. (Это делается на некоторых языках). Кастинг можно использовать, если желаемое поведение было желательным, но потеря некоторых бит была бы ясной.

Аналогично, i/j может запрашиваться из байта /short = > float или int/long = > double.