Питання Чому не Java = + =, - =, * =, / = комбіновані оператори призначення вимагають відливання?


До сьогодні я думав, що, наприклад:

i += j;

це просто ярлик для:

i = i + j;

Але що, якщо ми спробуємо це:

int i = 5;
long j = 8;

Потім i = i + j; не складатиметься але i += j; складемо добре.

Це означає, що насправді i += j; це ярлик на щось подібне i = (type of i) (i + j)?


3286
2018-01-03 10:10


походження


Я здивований тим, що Java це дозволяє, будучи жорсткішим мовою, ніж його попередники. Помилки при вилученні можуть призвести до критичної невдачі, як це було у випадку з Ariane5 Flight 501, коли 64-розрядна плаваюча передача у 16-розрядне ціле число призвела до аварії. - SQLDiver
У системі керування польотом, написаною на Java, це буде найменшим з ваших турбот @ SQLDiver - Ross Drew
Можливі дублікати Різниця між + = 10 і a = a + 10 в java? - phuclv
Насправді i+=(long)j; навіть зібрався добре. - Tharindu
Я думаю, що Дуглас Крокфорд завжди виграє аргумент ... Це поведінка страшно, і багато мов мають подібні проблеми навколо переходів. - Aluan Haddad


Відповіді:


Як завжди з цими питаннями, JLS тримає відповідь. В цьому випадку § 15.26.2 Оператори збірних об'єктів. Витяг:

Вираз складання призначення для форми E1 op= E2 еквівалентно E1 = (T)((E1) op (E2)), де T це тип E1, крім цього E1 оцінюється лише один раз.

Приклад, наведений з §15.26.2

[...] правильний наступний код:

short x = 3;
x += 4.6;

і призводить до того, що значення x має значення 7, оскільки воно еквівалентно:

short x = 3;
x = (short)(x + 4.6);

Іншими словами, ваше припущення є правильним.


2212
2018-01-03 10:15



Так i+=j складається, як я перевірив себе, але це призведе до втрати точності правильно? Якщо це так, чому це не дозволяє це статися в i = i + j теж? Чому там помилка? - bad_keypoints
@ronnieaka: Я гадаю, що дизайнери мови відчували, що в одному випадку (i += j), безпечніше вважати, що бажана втрата точності на відміну від іншого випадку (i = i + j) - Lukas Eder
Ні, це прямо там, напай на мене! Вибач, я не помітив це раніше. Як у вашому відповіді E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), так що це на кшталт неявного вниз typecasting (вниз від довго до int). Якщо в i = i + j, ми повинні зробити це явним чином, тобто забезпечити (T) участь у E1 = ((E1) op (E2)) Чи не так? - bad_keypoints
Я б припустив, що це використовується, щоб уникнути явного відтворення та / або f Постфікс в ситуації додавання подвійного літерального до короткого? - Andrey Akhmetov
Можлива причина того, чому компілятор Java додає типове значення, тому що якщо ви намагаєтеся виконувати арифметику на несумісні типи, то неможливо виконати типову частину результату за допомогою контрактної форми. Тип такого результату, як правило, більш точний, ніж типову частину проблемного аргументу. Жоден тип означає, що використання несумісних типів буде неефективним, оскільки це завжди призведе до компіляції викинути помилку. - ThePyroEagle


Хорошим прикладом такого відливання є використання * = або / =

byte b = 10;
b *= 5.7;
System.out.println(b); // prints 57

або

byte b = 100;
b /= 2.5;
System.out.println(b); // prints 40

або

char ch = '0';
ch *= 1.1;
System.out.println(ch); // prints '4'

або

char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'

442
2018-01-03 10:20



@SajalDutta Я не отримав цю посилання. Розум роз'яснюючи - Akshat Agarwal
@AkshatAgarwal ch є символом. 65 * 1.5 = 97,5 -> Ви отримали? - Sajal Dutta
Так, але я можу просто побачити якийсь початківець, приїжджаючи сюди, читаючи це, і пішовши, думаючи, що ви можете перетворити будь-який символ з верхнього регістру на нижній регістр, помноживши його на 1.5. - Dawood ibn Kareem
@DavidWallace Будь-який символ, якщо такий є A ;) - Peter Lawrey
@PeterLawrey & @DavidWallace Я покажу тобі таємниця- ch += 32  = D - Minhas Kamal


Дуже хороше питання. The Специфікація мови Java підтверджує вашу пропозицію.

Наприклад, правильний наступний код:

short x = 3;
x += 4.6;

і призводить до того, що значення x має значення 7, оскільки воно еквівалентно:

short x = 3;
x = (short)(x + 4.6);

223
2018-01-03 10:17



Або ще цікавіше: "int x = 33333333; x + = 1.0f;". - supercat


так,

в основному, коли ми пишемо

i += l; 

компілятор перетворює це на

i = (int)(i + l);

Я просто перевірив .class код файлу.

Дійсно, добре знати


163
2018-01-03 10:19



Чи можете ви сказати мені, який клас файл це? - Andrey Akhmetov
@ hexafraction: що ви маєте на увазі за класом файл? якщо ви запитаєте про файл класу, який я згадав у моєму повідомленні, аніж це сумісна версія вашого класу Java - Umesh Awasthi
О, ти згадав "код" файлу класу, що змусило мене повірити в те, що певний файл класу був задіяний. Я розумію, що ти маєш на увазі зараз. - Andrey Akhmetov
З усіх букв, ви дійсно вибрав "l" (нижній регістр L) ... - Bogdan Alexandru
@glglgl Я не погоджуюсь, що треба покладатися на шрифт, щоб розрізняти ці випадки ... але кожен має право вибрати те, що вважає найкращим. - Bogdan Alexandru


вам потрібно відкинути long до int  explicitly в випадку i = i + l  то він буде компілювати і дати правильний вихід. люблю

i = i + (int)l;

або

i = (int)((long)i + l); // this is what happens in case of += , dont need (long) casting since upper casting is done implicitly.

але у випадку += він просто працює добре, тому що оператор неявно робить відтворення типу від типу правильної змінної до типу лівий змінної, тому не потрібно явно виводити його.


85
2018-01-03 10:15



У такому випадку "неявне відлиття" може бути втратою. У реальності, як зазначає @LukasEder у своєму відповіді, кидається до int виконується після в +. Компілятор б (повинен?) Кинути попередження, якщо він дійсно кинув long до int. - Romain


Проблема тут передбачає відливання типу.

Коли ви додаєте int та long,

  1. Інтер-об'єкт роздано довго, і обидва додаються, і ви отримуєте довгий об'єкт.
  2. але довгий об'єкт не може бути неявно викинутим int. Отже, ви повинні зробити це явним чином.

Але += кодується таким чином, що він здійснює відтворення типу. i=(int)(i+m)


56
2018-01-03 10:20





У форматі Java перетворення виконуються автоматично, коли тип виразу з правої сторони операції присвоювання можна безпечно рекламувати до типу змінної на лівій стороні присвоєння. Таким чином ми можемо безпечно призначити:

 байт -> короткий -> int -> довгий -> float -> double. 

Це не буде працювати навпаки. Наприклад, ми не можемо автоматично перетворювати довгу на int, оскільки перший вимагає більшого обсягу пам'яті, ніж другий, і, отже, інформація може бути втрачена. Щоб примусити таку конверсію, ми повинні здійснити явне перетворення.
Тип - перетворення


47
2018-01-23 05:50



Привіт, але long в 2 рази більше, ніж float. - Sarge Borsch
А. float не може тримати все можливе int цінність і а double не може тримати все можливе long вартість - Alex MDC
Що ви маєте на увазі під "безпечно конвертованими"? З останньої частини відповіді я можу зробити висновок про те, що ви мали на увазі автоматичне перетворення (неявне відтворення), яке, звичайно, не відповідає дійсності у випадку плаваючого -> довгого. float pi = 3.14f; довгий b = pi; призведе до помилки компілятора. - Luke
Вам буде краще диференціювати примітивні типи з плаваючою точкою з цілими примітивними типами. Вони не однакові. - ThePyroEagle
Java має спрощені правила перетворення, які вимагають використання копій у багатьох моделях, де поведінка без відтінків в іншому випадку відповідає очікуванням, але не вимагає відтінків у багатьох шаблонах, які зазвичай є помилковими. Наприклад, компілятор прийме double d=33333333+1.0f; без скарги, навіть якщо результат 33333332.0, ймовірно, не буде тим, що мав на меті (до речі, арифметично правильна відповідь 33333334.0f може бути представлена ​​як float або int) - supercat