Perché gli operatori di assegnazione composti + =, - =, * =, / = Java non richiedono il cast?


3633

Fino ad oggi, ho pensato che per esempio:

i += j;

Era solo una scorciatoia per:

i = i + j;

Ma se proviamo questo:

int i = 5;
long j = 8;

Quindi i = i + j;non verrà compilato ma i += j;verrà compilato correttamente.

Vuol dire che in realtà i += j;è una scorciatoia per qualcosa di simile i = (type of i) (i + j)?


135
Sono sorpreso che Java lo permetta, essendo un linguaggio più rigoroso rispetto ai suoi predecessori. Gli errori nel casting possono portare a guasti critici, come nel caso del volo 501 di Ariane5, in cui un float a 64 bit trasmesso a un numero intero a 16 bit ha provocato il crash.
SQLDiver

103
In un sistema di controllo del volo scritto in Java, questa sarebbe l'ultima delle tue preoccupazioni @SQLDiver
Ross Drew,

10
In realtà i+=(long)j;anche compilerà bene.
Tharindu Sathischandra,

6
La costante spinta di un gruppo di sviluppatori per la precisione e un altro per la facilità d'uso è davvero interessante. Abbiamo quasi bisogno di due versioni del linguaggio, una incredibilmente precisa e una facile da usare. Spingendo Java da entrambe le direzioni si sposta verso l'essere inadatto per entrambi i gruppi.
Bill K,

5
se richiedesse il casting, dove lo metteresti? i += (int) f;lancia f prima dell'aggiunta, quindi non è equivalente. (int) i += f;lancia il risultato dopo l'assegnazione, neanche equivalente. non ci sarebbe posto per mettere un cast che significherebbe che vuoi lanciare il valore dopo l'aggiunta, ma prima dell'assegnazione.
Norill Tempest,

Risposte:


2441

Come sempre con queste domande, JLS ha la risposta. In questo caso §15.26.2 Operatori di assegnazione composti . Un estratto:

Un'espressione di assegnazione composta del modulo E1 op= E2è equivalente a E1 = (T)((E1) op (E2)), dov'è Til tipo di E1, tranne che E1viene valutata una sola volta.

Un esempio citato da §15.26.2

[...] il seguente codice è corretto:

short x = 3;
x += 4.6;

e risulta che x abbia il valore 7 perché equivale a:

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

In altre parole, il tuo presupposto è corretto.


42
Quindi i+=jcompila come mi sono controllato, ma si tradurrebbe in una perdita di precisione, giusto? In tal caso, perché non consente che accada anche in i = i + j? Perché ci bug lì?
bad_keypoints il

46
@ronnieaka: Immagino che i progettisti del linguaggio abbiano ritenuto che in un caso ( i += j), è più sicuro supporre che si desideri la perdita di precisione rispetto all'altro caso ( i = i + j)
Lukas Eder,

12
No, è proprio lì, davanti a me! Mi dispiace non averlo notato prima. Come nella tua risposta, E1 op= E2 is equivalent to E1 = (T)((E1) op (E2))quindi è un po 'come il downecasting implicito (da long a int). Considerando che in i = i + j, dobbiamo farlo esplicitamente, cioè fornire la (T)parte in E1 = ((E1) op (E2))Non è vero?
bad_keypoints,

11
Un probabile motivo per cui il compilatore Java aggiunge un typecast è perché se stai provando ad eseguire l'aritmetica su tipi incompatibili, non c'è modo di eseguire un typecast del risultato usando il modulo contrattato. Un typecast del risultato è generalmente più accurato di un typecast dell'argomento problematico. Nessun typecast renderebbe inutile la contrazione quando si usano tipi incompatibili, in quanto causerebbe sempre un errore nel compilatore.
ThePyroEagle il

6
Non è arrotondato. È lanciato (= troncato)
Lukas Eder

483

Un buon esempio di questo casting sta usando * = o / =

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

o

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

o

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

o

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

11
@AkshatAgarwal ch è un personaggio. 65 * 1.5 = 97.5 -> Capito?
Sajal Dutta,

79
Sì, ma vedo solo un principiante che arriva qui, legge questo e se ne va pensando che puoi convertire qualsiasi personaggio da maiuscolo a minuscolo moltiplicandolo per 1,5.
Dawood ibn Kareem,

103
@DavidWallace Qualsiasi personaggio purché sia A;)
Peter Lawrey,

14
@PeterLawrey & @DavidWallace Rivelerò il tuo segreto - ch += 32 = D
Minhas Kamal

256

Ottima domanda La specifica del linguaggio Java conferma il tuo suggerimento.

Ad esempio, il seguente codice è corretto:

short x = 3;
x += 4.6;

e risulta che x abbia il valore 7 perché equivale a:

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

15
O più divertente: "int x = 33333333; x + = 1.0f;".
supercat

5
@supercat, che trucco è questo? Una conversione allargata che viene arrotondata in modo errato, seguita da un'aggiunta che in realtà non cambia il risultato, passando a int di nuovo per produrre un risultato più inaspettato per le normali menti umane.
NeXus,

1
@neXus: IMHO, le regole di conversione avrebbero dovuto essere considerate un double->floatampliamento, sulla base del fatto che i valori di tipo floatidentificano i numeri reali in modo meno specifico rispetto a quelli di tipo double. Se uno viene visualizzato doublecome un indirizzo postale completo e floatcome un codice postale di 5 cifre, è possibile soddisfare una richiesta per un codice postale dato un indirizzo completo, ma non è possibile specificare con precisione una richiesta per un indirizzo completo dato solo un codice postale . La conversione di un indirizzo stradale in un codice postale è un'operazione con perdita di dati, ma ...
supercat

1
... qualcuno che ha bisogno di un indirizzo completo non dovrebbe in genere chiedere solo un codice postale. La conversione da float->doubleequivale a convertire il codice postale americano 90210 con "US Post Office, Beverly Hills CA 90210".
supercat,

181

Sì,

fondamentalmente quando scriviamo

i += l; 

il compilatore converte questo in

i = (int)(i + l);

Ho appena controllato il .classcodice del file.

Davvero una buona cosa da sapere


3
Puoi dirmi di quale file di classe si tratta?
nanofarad,

6
@hexafraction: cosa intendi per file di classe? se chiedi del file di classe che ho citato nel mio post, è la versione conforme della tua lezione di java
Umesh Awasthi,

3
Oh, hai menzionato "il" codice del file di classe, che mi ha portato a credere che fosse coinvolto un file di classe specifico. Capisco cosa intendi ora.
nanofarad,

@Bogdan Questo non dovrebbe essere un problema con i caratteri usati correttamente. Un programmatore che sceglie il carattere sbagliato per la programmazione dovrebbe chiaramente pensare a come procedere ...
glglgl,

7
@glglgl Non sono d'accordo sul fatto che si debba fare affidamento sul carattere per distinguere in quei casi ... ma tutti hanno la libertà di scegliere ciò che pensa sia meglio.
Bogdan Alexandru,

92

è necessario eseguire il cast da longa int explicitlyin caso i = i + l contrario, verrà compilato e restituito l'output corretto. piace

i = i + (int)l;

o

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

ma in questo caso +=funziona bene perché l'operatore esegue implicitamente il cast del tipo dal tipo di variabile destra al tipo di variabile sinistra, quindi non è necessario eseguire il cast esplicitamente.


7
In questo caso, il "cast implicito" potrebbe essere in perdita. In realtà, come afferma @LukasEder nella sua risposta, il cast di intviene eseguito dopo il +. Il compilatore sarebbe (dovrebbe?) Lanciare un avvertimento se davvero ha gettato la longa int.
Romain,

63

Il problema qui riguarda il cast di tipo.

Quando aggiungi int e long,

  1. L'oggetto int viene impostato su long ed entrambi vengono aggiunti e si ottiene l'oggetto long.
  2. ma l'oggetto lungo non può essere implicitamente assegnato a int. Quindi, devi farlo esplicitamente.

Ma +=è codificato in modo tale da digitare il casting.i=(int)(i+m)


54

In Java le conversioni di tipo vengono eseguite automaticamente quando il tipo di espressione sul lato destro di un'operazione di assegnazione può essere promosso in modo sicuro al tipo di variabile sul lato sinistro dell'assegnazione. Quindi possiamo tranquillamente assegnare:

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

Lo stesso non funzionerà al contrario. Ad esempio, non possiamo convertire automaticamente un long in un int perché il primo richiede più spazio di archiviazione del secondo e, di conseguenza, le informazioni potrebbero andare perse. Per forzare tale conversione dobbiamo effettuare una conversione esplicita.
Tipo: conversione


2
Ehi, ma longè 2 volte più grande di float.
Visualizza nome

11
A floatnon può contenere tutti i possibili intvalori e a doublenon può contenere tutti i possibili longvalori.
Alex MDC,

2
Cosa intendi con "convertito in sicurezza"? Da quest'ultima parte della risposta posso dedurre che intendevi la conversione automatica (cast implicito) che ovviamente non è vera in caso di float -> long. float pi = 3.14f; long b = pi; comporterà un errore del compilatore.
Luca

1
Sarebbe meglio differenziare i tipi primitivi in ​​virgola mobile con tipi primitivi interi. Non sono la stessa cosa.
ThePyroEagle il

Java ha regole di conversione semplicistiche che richiedono l'uso di cast in molti modelli in cui il comportamento senza cast altrimenti corrisponderebbe alle aspettative, ma non richiede cast in molti modelli che sono generalmente errati. Ad esempio, un compilatore accetterà double d=33333333+1.0f;senza lamentele, anche se il risultato 33333332.0 probabilmente non sarebbe quello che era previsto (per inciso, la risposta aritmeticamente corretta di 33333334.0f sarebbe rappresentabile come o floato int).
supercat

46

A volte, una simile domanda può essere posta durante un'intervista.

Ad esempio, quando scrivi:

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

non esiste un typecasting automatico. In C ++ non ci sarà alcun errore nella compilazione del codice sopra, ma in Java otterrai qualcosa del genere Incompatible type exception.

Quindi, per evitarlo, devi scrivere il tuo codice in questo modo:

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

6
Grazie per la comprensione del confronto tra l' opuso in C ++ e il suo utilizzo in Java. Mi piace sempre vedere questi frammenti di curiosità e penso che contribuiscano alla conversazione che potrebbero essere spesso esclusi.
Thomas,

2
Tuttavia, la domanda in sé è interessante, chiederlo in un'intervista è stupido. Non dimostra che la persona sia in grado di produrre un codice di buona qualità, ma dimostra solo che ha avuto abbastanza pazienza per prepararsi per un esame del certificato Oracle. E "evitare" i tipi incompatibili usando una pericolosa conversione automatica e nascondendo così il possibile errore di overflow, probabilmente dimostra persino che la persona non è in grado di produrre un probabile codice di buona qualità. Accidenti agli autori Java per tutte queste conversioni automatiche e auto-boxe e tutto il resto!
Honza Zidek,

25

La differenza principale è che con a = a + b, non è in corso il typecasting, quindi il compilatore si arrabbia con te per non aver fatto il typecasting. Ma con a += bciò che sta realmente facendo è la tipografia bin un tipo compatibile a. Quindi se lo fai

int a=5;
long b=10;
a+=b;
System.out.println(a);

Quello che stai davvero facendo è:

int a=5;
long b=10;
a=a+(int)b;
System.out.println(a);

5
Gli operatori di assegnazione composti eseguono una conversione restrittiva del risultato dell'operazione binaria, non dell'operando di destra. Quindi, nel tuo esempio, 'a + = b' non equivale a 'a = a + (int) b' ma, come spiegato in altre risposte qui, a 'a = (int) (a + b)'.
Lew Bloch,

13

Punto sottile qui ...

C'è un typecast implicito per i+jquando jè un doppio ed iè un int. Java SEMPRE converte un numero intero in un doppio quando c'è un'operazione tra di loro.

Per chiarire i+=jdove iè un numero intero ed jè un doppio può essere descritto come

i = <int>(<double>i + j)

Vedi: questa descrizione del casting implicito

Si potrebbe desiderare di typecast ja (int)in questo caso per la chiarezza.


1
Penso che potrebbe essere un caso più interessante int someInt = 16777217; float someFloat = 0.0f; someInt += someFloat;. L'aggiunta di zero a someIntnon dovrebbe influire sul suo valore, ma la promozione someInta floatpuò modificarne il valore.
supercat

5

Java Language Specification definisce E1 op= E2l'equivalente di E1 = (T) ((E1) op (E2))dove Tè un tipo di E1e E1viene valutato una volta .

Questa è una risposta tecnica, ma ti starai chiedendo perché sia ​​un caso. Bene, consideriamo il seguente programma.

public class PlusEquals {
    public static void main(String[] args) {
        byte a = 1;
        byte b = 2;
        a = a + b;
        System.out.println(a);
    }
}

Cosa stampa questo programma?

Hai indovinato 3? Peccato, questo programma non verrà compilato. Perché? Bene, succede che l'aggiunta di byte in Java sia definita per restituire unint . Credo che ciò sia dovuto al fatto che la Java Virtual Machine non definisce le operazioni di byte per il salvataggio su bytecode (dopo tutto c'è un numero limitato di quelle), l'uso delle operazioni di numeri interi è invece un dettaglio di implementazione esposto in una lingua.

Ma se a = a + bnon funziona, ciò significherebbe a += bche non funzionerebbe mai per i byte se E1 += E2fosse definito E1 = E1 + E2. Come mostra l'esempio precedente, sarebbe proprio così. Come hack per far +=funzionare l'operatore per byte e short, c'è un cast implicito. Non è un gran trucco, ma durante il lavoro di Java 1.0, l'obiettivo era quello di ottenere il rilascio del linguaggio per cominciare. Ora, a causa della compatibilità con le versioni precedenti, questo hack introdotto in Java 1.0 non può essere rimosso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.