Почему «int i = 2147483647 + 1;» Хорошо, но «байт b = 127 + 1;» не компилируется?


126

Почему это int i = 2147483647 + 1;нормально, но byte b = 127 + 1;не компилируется?


16
У меня тоже есть искренние сомнения: почему byteтип данных такой мучительный ?!
BoltClock

9
Это определенно ошибка дизайна, которая byteподписана вместо неподписанной.
непререкаемый

4
@BoltClock Боль только тогда, когда вы не знаете, как им правильно пользоваться. stackoverflow.com/questions/397867/…
starblue

2
@starblue, есть ли какой-нибудь реальный пример, где применим байтовый тип Java?
Thorbjørn Ravn Andersen

Если есть данные, указанные как байты, используйте Java byteдля ясности, например, в параметрах. В этом случае тот факт, что вы не можете присваивать intзначения, даже обнаружит некоторые ошибки. Или используйте byteдля экономии места в массивах. Я бы не стал использовать byteодно значение, которое просто помещается в байт.
starblue 01

Ответы:


172

Константы оцениваются как целые числа, поэтому 2147483647 + 1переполняются и дают вам новое целое число, которое назначается int, но при этом 127 + 1оценивается как intравное 128и не присваивается byte.


10
Вообще-то, сегодня я прочитал несколько головоломок на Java , в том числе загадку об этом ... См. Здесь: javapuzzlers.com/java-puzzlers-sampler.pdf - головоломка 3
MByD

3
Проблема заключается в типе intиз-за двоичного числового продвижения, значение 127- отвлекающий маневр.
starblue

Я бы предпочел, чтобы константы оценивались с бесконечной точностью, а также выдавали ошибку для int i = 2147483647 + 1;
Эдуардо

@MByD: Как вы сказали " while 127 + 1 also evaluated as int equals to 128, and it is not assignable to byte.", означает ли это, что 50 + 1 будет оцениваться как byteи, следовательно, может быть присвоено byte?
Bhushan

1
@ 10101010 - не совсем так. его можно будет присвоить байту, но сначала (согласно стандарту) он будет оцениваться как int.
MByD

35

Литерал 127 обозначает значение типа int. То же самое и с литералом 1. Сумма этих двух составляет целое число 128. Проблема во втором случае заключается в том, что вы присваиваете это переменной типа byte. Это не имеет ничего общего с фактическим значением выражений. Это связано с тем, что Java не поддерживает приведения (*). Вы должны добавить приведение типов

byte b = (byte)(127 + 1);

а затем компилируется.

(*) по крайней мере, не такого типа, как String-to-integer, float-to-Time, ... Java действительно поддерживает приведение, если оно в некотором смысле не является потерянным (Java называет это «расширением»).

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


Ваш пример кода, вероятно, должен быть byte b = (byte) 127 + 1; который является «Добавить 1 к максимальному байтовому значению», ваш пример просто превращает значение int 128 в байтовое значение.
NKCSS

6
@NKCSS - я не думаю, что вы правы, это - (byte)(127 + 1)приведение 128 (целое число) к байту, в то время как это (byte)127 + 1приведение 127 к байту, а затем снова к int, так как оно добавлено к 1 (int), а вы получить 128 (int) и ошибка остается.
MByD

6

В качестве доказательства @MByD:

Компилируется следующий код:

byte c = (byte)(127 + 1);

Потому что, хотя выражение (127 + 1)имеет byteтип int и выходит за пределы области видимости, результат приводится к типу byte. Это выражение производит -128.


3

JLS3 # 5.2 Преобразование присвоения

(переменная = выражение)

Кроме того, если выражение является постоянным выражением (§15.28) типа byte, short, char или int:

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


Без этого предложения мы не смогли бы написать

byte x = 0;
char c = 0;

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

byte x = (byte)0;

Что касается вопроса, сможем ли мы ... я не вижу ничего плохого, byte x = 0но опять же, я программист на C.
Grady Player

Возможно, я мог бы увидеть аргумент против char c = 0, но почему байт x = 0 неверен?
Майкл Бердж

Неопытный глаз вводит в заблуждение, думая, что они присваивают байтовый 0 байтовой переменной. В этом примере нет большого вреда, но в целом работа с байтами / короткими / символьными символами может сильно запутать из-за неявных преобразований. Они намного сложнее, чем думают люди. Я хочу как можно больше ясности в моем коде, не вводить неопределенности ради экономии нескольких нажатий клавиш.
непререкаемый 01

Применяется ли аналогичное правило, когда сужающееся примитивное преобразование происходит от long к int, например int i = 1 + 0L? Просто спрашиваю, потому что цитируемый вами текст явно не учитывает этот случай.
Эрвин Смаут, 03

@ Эрвин, нет, int i=0Lэто незаконно.
непререкаемый
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.