Pytanie Dlaczego operatory przypisania Java + =, - =, * =, / = złożone nie wymagają rzutowania?


Do dzisiaj myślałem, że na przykład:

i += j;

to skrót do:

i = i + j;

Ale co, jeśli spróbujemy tego:

int i = 5;
long j = 8;

Następnie i = i + j; nie będzie się kompilował, ale i += j; skompiluje się dobrze.

Czy to w rzeczywistości oznacza i += j; jest skrótem do czegoś takiego i = (type of i) (i + j)?


3286
2018-01-03 10:10


pochodzenie


Jestem zaskoczony, że Java pozwala na to, będąc bardziej surowym językiem niż jego poprzednicy. Błędy w odlewaniu mogą doprowadzić do krytycznej awarii, tak jak miało to miejsce w przypadku Ariane5 Flight 501, gdzie 64-bitowy rzut danych statycznych do 16-bitowej liczby całkowitej spowodował awarię. - SQLDiver
W systemie sterowania lotem napisanym w Javie byłby to najmniejszy z twoich problemów @SQLDiver - Ross Drew
Możliwy duplikat Różnica między + = 10 a a = a + 10 w java? - phuclv
Tak właściwie i+=(long)j; nawet skompiluje się dobrze. - Tharindu
Myślę, że Douglas Crockford wygrał tę dyskusję ... To zachowanie jest okropne, a wiele języków ma podobne problemy z konwersjami. - Aluan Haddad


Odpowiedzi:


Jak zwykle w przypadku tych pytań, JLS zawiera odpowiedź. W tym przypadku §15.26.2 Operatory przypisania złożonego. Ekstrakt:

Złożone wyrażenie przypisania formy E1 op= E2 jest równa E1 = (T)((E1) op (E2)), gdzie T jest typem E1, oprócz tego E1 jest oceniany tylko raz.

Cytowany przykład z §15.26.2

[...] następujący kod jest poprawny:

short x = 3;
x += 4.6;

i powoduje, że x ma wartość 7, ponieważ jest równoważne z:

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

Innymi słowy, twoje założenie jest poprawne.


2212
2018-01-03 10:15



Więc i+=j kompiluje się, gdy sam sprawdzałem, ale może to spowodować utratę precyzji, prawda? Jeśli tak, to dlaczego nie pozwala na to również w i = i + j? Po co nas tam wkurzyć? - bad_keypoints
@ronnieaka: Zgaduję, że projektanci języków czuli, że w jednym przypadku (i += j), bezpieczniej jest założyć, że pożądana jest utrata precyzji w przeciwieństwie do innego przypadku (i = i + j) - Lukas Eder
Nie, tam, na wprost mnie! Przepraszam, że wcześniej tego nie zauważyłem. Tak jak w twojej odpowiedzi, E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), więc jest to coś w rodzaju niejawnego rzucenia palenia (z długiego na int). Podczas gdy w i = i + j, musimy to zrobić jawnie, tj. Dostarczyć (T) udział w E1 = ((E1) op (E2)) Czyż nie? - bad_keypoints
Zakładam, że jest to używane do unikania jawnej obsady i / lub f Postfiks w sytuacji dodania podwójnego dosłownego do skrótu? - Andrey Akhmetov
Prawdopodobną przyczyną, dla której kompilator Java dodaje typografię, jest to, że jeśli próbujesz wykonywać arytmetyczne operacje na niekompatybilnych typach, nie ma możliwości wykonania typograficznej wersji wyników przy użyciu zakontraktowanego formularza. Typowy wynik jest z reguły bardziej dokładny niż typograficzny argument problematyczny. Żadna typografia nie uczyniłaby skurczu bezużytecznym w przypadku używania niekompatybilnych typów, ponieważ zawsze powodowałaby błąd kompilatora. - ThePyroEagle


Dobrym przykładem tego castingu jest użycie * = lub / =

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

lub

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

lub

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

lub

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

442
2018-01-03 10:20



@SajalDutta Nie dostałem tego odniesienia. Umysł wyjaśnia? - Akshat Agarwal
@AkshatAgarwal ch to char. 65 * 1,5 = 97,5 -> Rozumiesz? - Sajal Dutta
Tak, ale widzę, że jakiś początkujący przychodzi tutaj, czytając to i odchodząc, myśląc, że możesz przekształcić dowolny znak z wielkich liter na małe, pomnażając go przez 1,5. - Dawood ibn Kareem
@DavidWallace Dowolny znak tak długo, jak jest A ;) - Peter Lawrey
@PeterLawrey & @ DavidVallace Oddam ci twoje sekret- ch += 32  = D - Minhas Kamal


Bardzo dobre pytanie. The Specyfikacja języka Java potwierdza twoją sugestię.

Na przykład następujący kod jest poprawny:

short x = 3;
x += 4.6;

i powoduje, że x ma wartość 7, ponieważ jest równoważne z:

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

223
2018-01-03 10:17



Lub więcej zabawy: "int x = 33333333; x + = 1.0f;". - supercat


Tak,

w zasadzie kiedy piszemy

i += l; 

kompilator konwertuje to na

i = (int)(i + l);

Właśnie sprawdziłem .class kod pliku.

Naprawdę dobrze wiedzieć


163
2018-01-03 10:19



Czy możesz mi powiedzieć, który to jest plik klasy? - Andrey Akhmetov
@hexafraction: co rozumiesz przez plik klasy? jeśli pytasz o plik klasy, o którym wspomniałem w moim poście, to jest zgodna wersja twojej klasy java - Umesh Awasthi
Och, wspomniałeś o "tym" kodzie pliku klas, co doprowadziło mnie do przekonania, że ​​w grę wchodzi konkretny plik klasy. Rozumiem, co masz teraz na myśli. - Andrey Akhmetov
Ze wszystkich liter, wybrałeś "l" (małe litery L) ... - Bogdan Alexandru
@glglgl Nie zgadzam się, że w tych przypadkach trzeba polegać na czcionce ... ale każdy ma swobodę wyboru tego, co myśli najlepiej. - Bogdan Alexandru


musisz rzucić z long do int  explicitly w przypadku i = i + l  następnie skompiluje i poda poprawne wyniki. lubić

i = i + (int)l;

lub

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

ale w przypadku += działa po prostu poprawnie, ponieważ operator pośrednio wykonuje rzutowanie typu z prawej zmiennej na typ lewej zmiennej, więc nie trzeba jej jawnie rzutować.


85
2018-01-03 10:15



W takim przypadku "niejawna obsada" może być stratna. W rzeczywistości, jak @LukasEder stwierdza w swojej odpowiedzi, obsada int jest wykonywane po  +. Kompilator powinien (powinien?) Rzucić ostrzeżenie, jeśli rzeczywiście rzucił long do int. - Romain


Problem dotyczy tu odlewania typu.

Gdy dodasz int i long,

  1. Obiekt int jest rzutowany na długi i oba są dodawane, a ty dostajesz długi obiekt.
  2. ale długi obiekt nie może być niejawnie rzutowany na int. Musisz to zrobić wyraźnie.

Ale += jest kodowany w taki sposób, że wykonuje rzutowanie typu. i=(int)(i+m)


56
2018-01-03 10:20





W typach Java konwersje są wykonywane automatycznie, gdy typ wyrażenia po prawej stronie operacji przypisania może być bezpiecznie promowany do typu zmiennej po lewej stronie przypisania. W ten sposób możemy bezpiecznie przypisać:

 bajt -> short -> int -> long -> float -> double. 

To samo nie zadziała na odwrót. Na przykład nie możemy automatycznie przekonwertować długiego na int, ponieważ pierwszy wymaga więcej miejsca niż drugi, co powoduje utratę informacji. Aby wymusić takie nawrócenie, musimy dokonać wyraźnego nawrócenia.
Wpisz - Konwersja


47
2018-01-23 05:50



Hej, ale long jest 2 razy większy niż float. - Sarge Borsch
ZA float nie może trzymać wszystkich możliwych int wartość i double nie może trzymać wszystkich możliwych long wartość. - Alex MDC
Co masz na myśli przez "bezpiecznie skonwertowane"? Z drugiej części odpowiedzi mogę wywnioskować, że chodziło o automatyczną konwersję (niejawne rzutowanie), co oczywiście nie jest prawdą w przypadku float -> long. float pi = 3,14f; długo b = pi; spowoduje błąd kompilatora. - Luke
Lepiej odróżnić zmienne typu zmiennoprzecinkowego na typ pierwotny całkowity. To nie to samo. - ThePyroEagle
Java ma uproszczone reguły konwersji, które wymagają użycia rzutów w wielu wzorach, w których zachowanie bez rzutów byłoby zgodne z oczekiwaniami, ale nie wymaga rzutowania w wielu wzorcach, które są zazwyczaj błędne. Na przykład kompilator zaakceptuje double d=33333333+1.0f; bez skargi, mimo że wynik 33333332.0 prawdopodobnie nie byłby tym, co zamierzano (nawiasem mówiąc, poprawna arytmetycznie odpowiedź 33333334.0f byłaby reprezentowalna jako albo float lub int). - supercat


Czasami takie pytanie można zadać podczas wywiadu.

Na przykład, gdy piszesz:

int a = 2;
long b = 3;
a = a + b;

nie ma automatycznego typowania. W C ++ nie będzie żadnego błędu kompilującego powyższy kod, ale w Javie otrzymasz coś takiego Incompatible type exception.

Aby tego uniknąć, musisz napisać swój kod w ten sposób:

int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting

37
2017-12-02 10:40



Dzięki za wgląd w porównanie z op używać w C ++ do jego używania w Javie. Zawsze lubię oglądać te drobiazgi i myślę, że przyczyniają się one do rozmowy, która często może zostać pominięta. - Thomas
Jednak samo pytanie jest ciekawe, zadając to w wywiadzie jest głupi. Nie dowodzi to, że dana osoba może wytworzyć kod dobrej jakości - to tylko dowodzi, że miał dość cierpliwości, aby przygotować się do egzaminu certyfikacyjnego Oracle. "Unikanie" niekompatybilnych typów za pomocą niebezpiecznej automatycznej konwersji, a tym samym ukrywanie ewentualnego błędu przepełnienia, prawdopodobnie nawet udowadnia że dana osoba nie jest w stanie wytworzyć prawdopodobnego kodu dobrej jakości. Cholerstwo autorów Java dla wszystkich tych automatycznych konwersji i auto-boxingu i wszystkiego! - Honza Zidek