Несоответствие типов с байтовой переменной с использованием тернарного оператора - PullRequest
2 голосов
/ 20 января 2020
int a = 10, b = 20;
byte x = (a>b) ? 10 : 20;

В предыдущем коде выдается ошибка времени компиляции: несоответствие типов не может быть преобразовано из int в байт.

Странно, что когда я заменяю выражение (a> b) на (true), код успешно компилируется! Также, когда я заменяю выражение литералами (10> 20), код также работает !!

Не только это, но и когда я явно набираю приведение 10, 20 или даже весь троичный оператор, код тоже работает!

byte x = (a>b) ? (byte)10 : 20;
byte x = (a>b) ? 10 : (byte)20;
byte x = (byte)((a>b) ? 10 : 20);

Что именно не так с выражением (a> b)?

Обратите внимание, что эквивалентный код, использующий if-else, работает нормально.

int a = 10, b = 20;
byte x;
if(a>b) {
    x = 10;
} else {
    x = 20;
}

Ответы [ 4 ]

2 голосов
/ 20 января 2020

Поскольку, когда выражение напрямую указывается с помощью чисел, таких как (10>20), выражение вычисляется во время самой компиляции и значение результата присваивается байтовой переменной. Если вы используете любую IDE, вы можете увидеть предупреждение Dead code в этом выражении

byte x = (20>10) ? 10 : 20 (Dead code); // because compiler know 20 greater than 10 and assigns 10 to x

Но при использовании переменных a,b компилятор не знает этих значений во время компиляции, а выражение оценивается во время выполнения , Так как в java по умолчанию числовые значения c представлены как int, он запрашивает явное приведение типов

byte x = (a>b) ? 10 : 20;  //Type mismatch: cannot convert from int to byte

И я бы предложил прочитать это для приведение типов с троичным оператором

0 голосов
/ 23 января 2020

Это может быть излишним со всеми остальными ответами, но я так убедил себя. a.compareTo(b) явно не может быть вычислено во время компиляции, и целочисленные литералы всегда по умолчанию int:

String a = "a", b = "b";
byte x = (a.compareTo(b) > 1) ? 10 : 20;

выдает ту же ошибку времени компиляции:

error: incompatible types: possible lossy conversion from int to byte
    byte x = (a.compareTo(b) > 1) ? 10 : 20;
                                  ^

Плавающие точки двойной по умолчанию, так что это также приводит к аналогичной ошибке:

float x = (a.compareTo(b) > 1) ? 10.0 : 20.0;
0 голосов
/ 20 января 2020

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

Но при сборке всей информации кажется, что:

byte x = (a>b) ? 10 : 20;

выдает ошибку, потому что существует неявное преобразование типа int в byte.
В более новых java версий, показанных компилятором:

error: incompatible types: possible lossy conversion from int to byte

Это потому, что второй и третий операнд в (a>b) ? 10 : 20 рассматривается как int. Константы basei c цифра c всегда оцениваются как int, и их необходимо явно уменьшить до byte. Приведение возвращаемых (второго или третьего) операндов или целого оператора к байту предотвращает ошибку, поскольку он явно показывает компилятору, что потеря данных при преобразовании от int к byte не должна приниматься во внимание.

Приведение второго или третьего операнда к byte прямо говорит, что каждое возвращение от этого троичного оператора должно рассматриваться как byte.

Запись чего-то вроде:

int aa = 100000000;
byte zz = aa;
//or
double dd = 10.11d;
long xyx = dd;

приведет к тому же виду ошибки.

Причина всего этого в том, что продвижение меньшего примитива к верхнему не влияет на детерминированный c результат программы, но снижает (т. е. int на short) или отбрасывание плавающей запятой может привести к различным результатам.
Обратите внимание, что такие объявления не приводят к ошибке:

byte ooo = 100; //it throws an error if the value of ooo is higher than 127
//cause the `127` is the max value for byte type
int iii = ooo;

Строка, подобная:

byte x = (20>10) ? 10 : 20;

не выдает никаких ошибок времени выполнения / ошибки компилятора, потому что при наличии явных значений, предоставленных компилятору условия, можно просто оценить условие, которое приводит к: byte x = 10. Мертвый код, то есть недостижимый оператор в Java, может быть получен с:

try {
    throw new Exception();
} catch (Exception e) {
    throw new Exception(e);
    System.out.println(); //compiler shows this line as unreachable
}

Таким образом, для краткого заключения числовые литералы / константы оцениваются как тип int, если они не являются явными присваивается указанному типу c. byte x = 10; работает, потому что он явно говорит о назначении 10 байтовому типу, а 10 относится к типу byte и не приводит к потере данных (назначение -129 или 10.1 throws ошибка).

С byte x = (a>b) ? 10 : 20; дело в том, что все троичное выражение не вычисляется на лету, компилятор не знает, не изменились ли значения a и b где-то еще , Указание явных чисел или просто true/false в условии троичного оператора делает результат выражения очевидным для компилятора (и для глаз разработчика).

После более тщательного изучения spe c из условного оператора он говорит:

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

С учетом сказанного, выбранный оператор результата оценивается ONLY , когда оператор условия оцененный (1-й результат, если истина, 2-й, если ложь).
Явное условие, такое как 20>10 или true, оценивается во время компиляции, поэтому точное, явное значение присваивается в случае byte x = ....

Почему что-то такое маленькое, как:

int a = 10, b = 20;
byte x = (a>b) ? 10 : 20;

не оценивается во время компиляции и выдает ошибку?
Поскольку уже указанные числовые литералы оцениваются как int и в вышеприведенном присваивании переменной x переменная не является явной (напоминание, что выбранный оператор результата вычисляется после оценки условия).
Компилятор не является чем-то вроде полного анализатора кода c, пытающегося запросить это может привести к чрезмерно усложненному байт-коду.
Представьте себе более сложный пример, в котором значения a и b инициализируются в коде, но есть несколько операторов if, которые могут изменить значения, присвоенные a или b. Затем компилятор должен сначала проверить, можно ли выполнить какой-либо из операторов if во время компиляции, чтобы определить, существует ли значение времени компиляции для одной из этих переменных, а затем создать условия для ternary/conditional operator на основе этого, если одно из этих значений изменилось. И в результате мы получим много очень сложного кода, что обойдется без такого анализа.

Это очень простой пример, поэтому для разработчика он может выглядеть как wtf, но для предотвращения компиляции такой случай будет слишком трудоемким, поскольку компилятор не может определить, прост ли предоставленный код или нет и не может оценить значения переменных для условий во время компиляции.

0 голосов
/ 20 января 2020

По той же причине вы можете сделать это:

        int a = 10;
        byte x = 10;
        System.out.println("a: " + x);

, но вы не можете сделать это:

        int a = 10;
        byte x = a; <-- java: incompatible types: possible lossy conversion from int to byte
        System.out.println("a: " + x);

Прямое присвоение int byte использование литерала является сужающим преобразованием. Это не нарушает систему типов, потому что Java с удовольствием отбросит значащие биты из буквального 10 и подарит вам byte.

Но если вы скажете, что a - это целое число, оно не может быть byte (потому что byte не равно и int).

...