Che cosa significa “ansible perdita di conversione” e come posso risolverlo?

I nuovi programmatori Java sono spesso confusi da messaggi di errore di compilazione come:

“Tipi incompatibili: ansible conversione lossy da double a int”

per questa riga di codice:

int squareRoot = Math.sqrt(i); 

Che cosa significa questo errore e come lo risolvi?

Prima di tutto, questo è un errore di compilazione. Se lo si vede in un messaggio di eccezione in fase di esecuzione, è perché si è eseguito un programma con errori di compilazione 1 .

La forma generale del messaggio è questa:

“tipi incompatibili: ansible conversione con perdita da a

dove e sono entrambi tipi numerici primitivi; cioè uno di byte , char , short , int , long , float o double .

Questo errore si verifica quando il codice tenta di eseguire una conversione implicita da a ma la conversione potrebbe essere perdita .

Nell’esempio nella domanda:

  int squareRoot = Math.sqrt(i); 

il metodo sqrt produce un double , ma una conversione da double a int è potenzialmente lossy.

Cosa significa “potenzialmente lossy”?

Bene, guardiamo un paio di esempi.

  1. Una conversione da long ad int è una conversione potenzialmente lossy perché ci sono valori long che non hanno un valore int corrispondente. Ad esempio, qualsiasi valore long maggiore di 2 ^ 31 – 1 è troppo grande per essere rappresentato come int . Allo stesso modo, qualsiasi numero inferiore a -2 ^ 31 è troppo piccolo.

  2. Una conversione da un int a long NON è una conversione con perdita perché ogni valore int ha un valore long corrispondente.

  3. Una conversione di un float a long è una conversione potenzialmente lossy perché i valori float sono troppo grandi o troppo piccoli per rappresentare valori long .

  4. Una conversione da long a float NON è una conversione lossy perché ogni valore long ha un valore float corrispondente. (Il valore convertito può essere meno preciso, ma “perdita” non significa che … in questo contesto.)

Queste sono tutte le conversioni potenzialmente in perdita:

  • short a byte o char
  • char a byte o short
  • int a byte , short o char
  • long to byte , short , char o int
  • float to byte , short , char , int o long
  • double a byte , short , char , int , long o float .

Come risolvi l’errore?

Il modo per far sparire l’errore di compilazione è aggiungere un typecast. Per esempio;

  int i = 47; int squareRoot = Math.sqrt(i); // compilation error! 

diventa

  int i = 47; int squareRoot = (int) Math.sqrt(i); // no compilation error 

Ma è davvero una soluzione? Si consideri che la radice quadrata di 47 è 6.8556546004 … ma squareRoot otterrà il valore 6 . (La conversione verrà troncata, non rotonda.)

E che mi dici di questo?

  byte b = (int) 512; 

Ciò si traduce in b ottenendo il valore 0 . La conversione da un tipo int più grande a un tipo int più piccolo viene eseguita nascondendo i bit di ordine superiore e gli 8 bit di 512 di ordine inferiore sono tutti a zero.

In breve, non dovresti semplicemente aggiungere un typecast, perché potrebbe non fare la cosa giusta per la tua applicazione .

Invece, devi capire perché il tuo codice deve fare una conversione:

  • Sta succedendo perché hai fatto qualche altro errore nel tuo codice?
  • Il dovrebbe essere di un tipo diverso, quindi non è necessaria una conversione lossy qui?
  • Se è necessaria una conversione, è la conversione lossy silenziosa che il typecast farà il comportamento corretto?
  • O il tuo codice dovrebbe eseguire alcuni controlli di intervallo e gestire valori errati / imprevisti generando un’eccezione?

Alcuni casi specifici:

“Possibile conversione lossy” quando si sottoscrive un abbonamento.

Primo esempio:

 for (double d = 0; d < 10.0; d += 1.0) { System.out.println(array[d]); // <<-- possible lossy conversion } 

Il problema qui è che il valore dell'indice dell'array deve essere int . Quindi d deve essere convertito da double a int . In generale, l'uso di un valore in virgola mobile come indice non ha senso. O qualcuno ha l'impressione che gli array Java funzionino come (per esempio) i dizionari Python, o hanno trascurato il fatto che l'aritmetica in virgola mobile è spesso inesatta.

La soluzione è riscrivere il codice per evitare di utilizzare un valore in virgola mobile come indice di array. (Aggiungere un cast di tipo è probabilmente una soluzione errata.)

Secondo esempio:

 for (long l = 0; l < 10; l++) { System.out.println(array[l]); // <<-- possible lossy conversion } 

Questa è una variazione del problema precedente e la soluzione è la stessa. La differenza è che la causa principale è che gli array Java sono limitati a indici a 32 bit. Se si desidera una struttura dati "array like" con più di 2 elementi 31 - 1, è necessario definire o trovare una class per farlo.


1 - Ad esempio, l'IDE di Eclipse ha un'opzione che consente di ignorare gli errori di compilazione ed eseguire comunque il codice. Se si seleziona questo, il compilatore dell'IDE creerà un file .class cui il metodo con l'errore genererà un'eccezione non controllata se viene chiamato. Il messaggio di eccezione menzionerà il messaggio di errore di compilazione.