обзор
Когда вы выполняете унарную или двоичную операцию в Java, стандартным поведением является использование самого широкого операнда (или более широкого операнда для byte , short char и char ) Это легко понять, но может сбить с толку, если вы подумаете о том, каким может быть оптимальный тип.
умножение
Когда вы выполняете умножение, вы часто получаете намного большее число, чем любое из отдельных чисел по величине. то есть | a * b | >> | а | и | a * b | >> | б | часто бывает. И для маленьких типов это работает как ожидалось.
Рассмотрим эту программу:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public static void main(String[] args) throws IOException { System.out.println(is(Byte.MAX_VALUE * Byte.MAX_VALUE)); System.out.println(is(Short.MAX_VALUE * Short.MAX_VALUE)); System.out.println(is(Character.MAX_VALUE * Character.MAX_VALUE)); System.out.println(is(Integer.MAX_VALUE * Integer.MAX_VALUE)); System.out.println(is(Long.MAX_VALUE * Long.MAX_VALUE));}static String is(byte b) { return "byte: " + b;}static String is(char ch) { return "char: " + ch;}static String is(short i) { return "short: " + i;}static String is(int i) { return "int: " + i;}static String is(long l) { return "long: " + l;} |
который печатает:
|
1
2
3
4
5
|
int: 16129int: 1073676289int: -131071int: 1long: 1 |
Только byte * byte и short * short не переполняются, поскольку они были расширены. char * char не является значимой операцией, даже если она разрешена. Но int * int переполняется, хотя у нас есть длинный тип, который может хранить это значение без переполнения. И byte и short расширяются неявно, но не int . long действительно должен быть расширен, но у нас нет более широкого типа примитивов, который имел бы смысл когда-то, однако 64-битный примитив не кажется таким длинным в наши дни.
разделение
Деление немного странно в том смысле, что делитель может расширить результат. Наличие более широкого делителя, чем у числителя, не означает, что результат будет больше (но обычно он меньше)
|
1
2
3
4
5
|
System.out.println(is(Byte.MAX_VALUE / (byte) 1));System.out.println(is(Byte.MAX_VALUE / (short) 1));System.out.println(is(Byte.MAX_VALUE / (char) 1));System.out.println(is(Byte.MAX_VALUE / (int) 1));System.out.println(is(Byte.MAX_VALUE/ (long) 1)); |
печать
|
1
2
3
4
5
|
int: 127int: 127int: 127int: 127long: 127 |
Когда вы делите byte/byte вы получаете int даже если вы не можете получить значение больше byte . (если вы не разделите Byte.MIN_VALUE на -1, в этом случае будет иметь место short ), и если вы разделите byte/long вы получите long даже если значение по-прежнему не может быть больше byte .
модуль
Когда вы выполняете модуль a % b , результат не может быть больше, чем b . И все же модуль будет шире, а не уменьшит результат.
|
01
02
03
04
05
06
07
08
09
10
11
|
System.out.println(is(Byte.MAX_VALUE % Byte.MAX_VALUE));System.out.println(is(Byte.MAX_VALUE % Short.MAX_VALUE));System.out.println(is(Byte.MAX_VALUE % Character.MAX_VALUE));System.out.println(is(Byte.MAX_VALUE % Integer.MAX_VALUE));System.out.println(is(Byte.MAX_VALUE % Long.MAX_VALUE)); System.out.println(is(Byte.MAX_VALUE % (byte) 2));System.out.println(is(Short.MAX_VALUE % (byte) 2));System.out.println(is(Character.MAX_VALUE % (byte) 2));System.out.println(is(Integer.MAX_VALUE % (byte) 2));System.out.println(is(Long.MAX_VALUE % (byte) 2)); |
печать
|
01
02
03
04
05
06
07
08
09
10
|
int: 0int: 127int: 127int: 127long: 127int: 1int: 1int: 1int: 1long: 1 |
Если вы модулируете X числом, результат не может быть больше / больше, чем X , он может быть только меньше. Тем не менее, JLS say он должен стать шире. Если вы модулируете X byte , результат может быть только в диапазоне byte .
Я также упомянул унарные операции, и, возможно, самый простой — унарный минус.
|
1
2
3
4
5
|
System.out.println(is(-Byte.MIN_VALUE));System.out.println(is(-Short.MIN_VALUE));System.out.println(is(-Character.MIN_VALUE));System.out.println(is(-Integer.MIN_VALUE));System.out.println(is(-Long.MIN_VALUE)); |
печать
|
1
2
3
4
5
|
int: 128int: 32768int: 0int: -2147483648long: -9223372036854775808 |
В первых трех случаях тип расширен. byte может быть расширен до short , но это правильно как int . Однако для int и long он не расширен, и вы можете получить редкое переполнение.
Немного шансов — унарный плюс, который не меняет значение (и, следовательно, не может изменить его диапазон), но может расширить значение.
|
1
2
3
4
5
|
System.out.println(is(+Byte.MIN_VALUE));System.out.println(is(+Short.MIN_VALUE));System.out.println(is(+Character.MIN_VALUE));System.out.println(is(+Integer.MIN_VALUE));System.out.println(is(+Long.MIN_VALUE)); |
печать
|
1
2
3
4
5
|
int: -128int: -32768int: 0int: -2147483648long: -9223372036854775808 |
Можем ли мы это исправить?
К сожалению нет. Слишком много кода зависит от этой логики. Например, скажем, вы пишете что-то вроде этого.
|
1
2
3
|
long i = ...byte b = ...long l = i % b + Integer.MAX_VALUE; |
Если бы я% b превратился из long в byte , это выражение могло бы переполниться.
Вывод
Java может расширять некоторые значения, когда это необходимо, но также не может расширять некоторые int операции, которые действительно должны быть long . Это никогда не даст более узкого результата, даже если это может быть более логичным.
Нам нужно знать о крайних случаях, в частности int * int , и знать, как расширять их самостоятельно, когда мы видим такую операцию. например
|
1
|
long l = (long) a * b; |
Если мы не уверены, что a * b будет соответствовать значению int .
| Ссылка: | Непоследовательная работа расширяет правила в Java от нашего партнера JCG Питера Лоури из блога Vanilla Java . |