«Несоответствие типов: невозможно преобразовать int в byte»

Я видел, как люди задавали вопросы об ошибке «Несоответствие типов: невозможно преобразовать int в byte». Но они в основном вызваны арифметическими операциями.

Вот мой случай:
(это происходит, когда я хочу поиграть с битами в Eclipse Kepler)

//java 7 binary literals

byte a =  0b01111111; //8-bit it compiles 

byte b =  0b10000000;  //8-bit error: Type mismatch: cannot convert int to byte.                        

byte c =  (byte) 0b10000000; //8-bit it works fine if casted.

Дело в том, что если это 8 бит и старшая цифра 1, то компилятор выдает ошибку. Я хочу знать, почему. Префикс 0b означает, что это двоичный литерал, так почему компилятор принимает самую старшую цифру как цифру со знаком или что-то в этом роде?

Спасибо за ответ.

[Редактировать3:]

byte a = -128; //a = 0xFF = 11111111 (8 bits), compiler says ok.
byte b = 0b11111111; //compiler error

[Edit2: побитовая и операция также каким-то образом вызывает ошибку]

byte a = 0b00000000;  //8 bits
a = (a&0xFF);  //gives same error: Type mismatch: cannot convert int to byte
a = (byte)(a&0xFF); //it is fine to cast

[Edit1: скриншот обновлен] введите здесь описание изображения

введите здесь описание изображения


person Weishi Z    schedule 17.11.2013    source источник


Ответы (3)


Вы правы, подозревая, что речь идет о целых числах со знаком. В Java ВСЕ типы целых чисел (byte, short, int, long) ВСЕГДА подписаны. Java использовала дополнение до двух для хранения подписанных (читай «всех») значений. В основном это означает, что если первый бит любого типа (не первый бит, указанный в литерале, а первый сохраненный бит) равен 1, число отрицательное. Если это 0, это положительно.

Второе, что важно: в Java нет литералов BYTE. Есть int литералов и long литералов. Каждое записываемое число (будь то двоичное (префикс 0b), восьмеричное (префикс 0), десятичное (без префикса) или шестнадцатеричное (префикс 0x)) является целочисленным литералом, если вы не добавляете L (нижний, либо верхний регистр), это long. Невозможно напрямую записать short или byte.

Теперь это означает, что все те примеры, которые вы записали, сначала создают int. Вы не создаете там bytes.

Итак, последняя часть: что произойдет, если вы попытаетесь сохранить это int внутри byte - без приведения или с приведением. Если вы выполняете кастинг явно, вы в основном указываете Java просто игнорировать любые биты, которые не подходят. Они будут обрезаны — даже если это изменит значение числа (примеры см. ниже). Если вы не бросите, биты все равно будут вырезаны. Но Java не будет этого делать, если изменит значение, чтобы убедиться, что вы действительно имеете в виду то, что делаете.

Чтобы связать все это с примерами из вопроса:
int 0b01111111 равно 127
byte 0b01111111 равно 127
-> преобразование возможно без переполнения, поэтому Java сделает это даже без явного приведения

int 0b10000000 is 128
byte 0b10000000 is -128
-> Переполнение при преобразовании, поэтому Java выдаст ошибку, если нет явного приведения.

person Johannes H.    schedule 17.11.2013
comment
@jprofitt: О. Снап, ты тут как тут. Это... может изменить ответ... Надо почитать об этом. - person Johannes H.; 17.11.2013
comment
На самом деле, теперь я еще больше запутался. Согласно спецификации языка, байтовых литералов не существует. Так что ни один из этих случаев не должен работать... - person Johannes H.; 17.11.2013
comment
Спасибо за помощь! Я тоже считал цифры 2 минуты назад... Я должен указать длину в комментариях, чтобы улучшить читаемость. - person Weishi Z; 17.11.2013
comment
@ЙоханнесХ. : бинарные литералы появились в Java 7. Поэтому в более ранних версиях они будут недоступны. - person Lion; 17.11.2013
comment
Спасибо за подсказку! Это означает, что мне придется изучить спецификацию Java 7. - person Johannes H.; 17.11.2013
comment
@Лев: Ах. Нет. Вы неправильно прочитали. Меня смущают не бинарные литералы, а BYTE-литералы. Все литералы всегда должны быть типа int, long можно указать, добавив префикс l. Short и byte должны быть приведены. Всегда. - person Johannes H.; 17.11.2013
comment
@ЙоханнесХ. : я не знаю точной терминологии, но я только что поместил этот оператор byte b = 0b10000000; в IDE NetBeans с jdk 6, он дал эту ошибку компиляции - binary literals are not supported in -source 1.6 (use -source 7 or higher to enable binary literals) - person Lion; 17.11.2013
comment
Что меня смущает, так это то, что ЛЮБОЙ целочисленный литерал всегда должен создавать int. Нет byte. Таким образом, присвоение литерала типу byte без приведения всегда должно заканчиваться неудачей, - person Johannes H.; 17.11.2013
comment
Понял. @richard кажется прав. Речь идет не о том, как вы можете указывать байты, а о том, как вы НЕ можете их указывать, и о том, как работает преобразование из int в byte. Смотрите мой отредактированный ответ. - person Johannes H.; 17.11.2013
comment
@ЙоханнесХ. Разве это не должно быть добавлено L, а не добавлено L? - person Dennis Meng; 17.11.2013
comment
@DennisMeng так и должно быть ;) - person Johannes H.; 17.11.2013
comment
@ЙоханнесХ. В представлении с дополнением до двух, скажем, только для 8 бит, 0b10000000 равно -128, что все еще находится в диапазоне 8-битного числа с дополнением до двух. Так что я не понял, почему есть переполнение. И не могли бы вы отредактировать ответ, указав, что происходит, когда вы выполняете явное преобразование в последней строке? (что такое преобразованный байт) - person Weishi Z; 18.11.2013
comment
@Lion Это потому, что двоичные литералы с префиксом 0b поддерживаются только после java 7. Поэтому вам нужно загрузить java 7 и выбрать JRE до 1.7. - person Weishi Z; 18.11.2013
comment
Получил вашу точку зрения. В Java нет литералов BYTE. Есть литералы int и литералы long. Это позволяет мне полностью понять все это. - person Weishi Z; 02.12.2013
comment
Здорово! Приятно слышать, что вы поняли, в конце концов, рад, что смог помочь. Все еще хороший вопрос, мне потребовалось некоторое время, чтобы понять это самому ^^ - person Johannes H.; 03.12.2013

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

person richard    schedule 17.11.2013
comment
В двух комплиментарном представлении 0b10000000 равно -128, что все еще находится в диапазоне int в java. - person Weishi Z; 17.11.2013
comment
Верно, но вы пытались сделать байт, отсюда и ошибка. Байт типа не может содержать значение 0b10000000 - person richard; 17.11.2013
comment
+1 за лидерство здесь, я согласен, это должен быть ответ. Добавил немного больше деталей к этому, см. мой ответ. - person Johannes H.; 17.11.2013
comment
Да, проблема заключается в отсутствии двоичного литерала. +1 - person richard; 17.11.2013
comment
Я так и не понял, почему байт не может содержать значение 0b10000000? Это 8 бит. Он должен представлять некоторую ценность, верно? Почему переполнение? - person Weishi Z; 18.11.2013
comment
@WeishiZeng Смотрите мой ответ. Это потому, что если значение хранится в байте, первый бит равен 1, поэтому это отрицательное значение. Если он хранится в int, первый бит равен нулю (не указанному в вашем литерале) и, следовательно, положительному числу. Если вы сохраните целое число в байте, знак изменится — это то, что вы называете переполнением. - person Johannes H.; 19.11.2013

Байтовые переменные МОГУТ содержать значение 0b1000000, но поскольку они подписаны, это представляет собой целочисленное значение -128. Причина, по которой он не может преобразовать литерал, заключается в том, что когда вы записываете его как литерал без приведения, компилятор видит его как (int) 0b10000000, что является целочисленным значением POSITIVE 128.

Любое целочисленное значение выше 127 выходит за пределы байта, так как байты подписаны и могут содержать только целые значения от -128 до 127. Вот где возникает переполнение (чтобы удержать 128, вам понадобится девятый бит для знака ). Каждый раз, когда значение со знаком имеет 1 в качестве старшего бита, оно представляет отрицательное число, поэтому, чтобы поместить число, подобное 0b10000000, в байт, вам нужно представить отрицательное значение в вашем литерале. Например, значение -128 будет эквивалентно int 0b11111111111111111111111110000000, поэтому вам нужно будет использовать его как литерал или, что намного проще, просто явно привести его к байту, например: (byte) 0b10000000

person user3489270    schedule 02.04.2014