java - ¿Qué significa "posible conversión con pérdida" y cómo lo arreglo?
compiler-errors (1)
Los nuevos programadores de Java a menudo se confunden con los mensajes de error de compilación como:
"Tipos incompatibles: posible conversión con pérdida de doble a int"
para esta línea de código:
int squareRoot = Math.sqrt(i);
En general, ¿qué significa el mensaje de error "posible conversión con pérdida" y cómo se soluciona?
En primer lugar, este es un error de compilación. Si alguna vez lo ve en un mensaje de excepción en tiempo de ejecución, es porque ha ejecutado un programa con errores de compilación 1 .
La forma general del mensaje es esta:
"tipos incompatibles: posible conversión con pérdida de
<type1>
a<type2>
"
donde
<type1>
y
<type2>
son tipos numéricos primitivos;
es decir, uno de
byte
,
char
,
short
,
int
,
long
,
float
o
double
.
Este error ocurre cuando su código intenta realizar una conversión
implícita
de
<type1>
a
<type2>
pero la conversión podría tener
pérdidas
.
En el ejemplo de la pregunta:
int squareRoot = Math.sqrt(i);
El método
sqrt
produce un
double
, pero una conversión de
double
a
int
es potencialmente deficiente.
¿Qué significa "potencialmente con pérdida"?
Bueno, veamos un par de ejemplos.
-
Una conversión de un
long
a unint
es una conversión con pérdida potencial porque hay valoreslong
que no tienen un valorint
correspondiente. Por ejemplo, cualquier valorlong
que sea mayor que 2 ^ 31 - 1 es demasiado grande para ser representado como unint
. Del mismo modo, cualquier número menor que -2 ^ 31 es demasiado pequeño. -
Una conversión de un
int
a unlong
NO es una conversión con pérdida porque cada valorint
tiene un valorlong
correspondiente. -
Una conversión de un
float
a unlong
es una conversión con pérdida potencial porque existen valoresfloat
que son demasiado grandes o demasiado pequeños para representar valores tanlong
. -
Una conversión de un valor
long
a un valorfloat
NO es una conversión con pérdida porque cada valorlong
tiene un valorfloat
correspondiente. (El valor convertido puede ser menos preciso, pero "pérdida" no significa que ... en este contexto).
Estas son todas las conversiones que potencialmente tienen pérdidas:
-
short
abyte
ochar
-
char
abyte
oshort
-
int
byte
,short
ochar
-
long
abyte
,short
,char
oint
-
float
abyte
,short
,char
,int
olong
-
double
abyte
,short
,char
,int
,long
ofloat
.
¿Cómo arreglas el error?
La forma de hacer que desaparezca el error de compilación es agregar un encasillado. Por ejemplo;
int i = 47;
int squareRoot = Math.sqrt(i); // compilation error!
se convierte en
int i = 47;
int squareRoot = (int) Math.sqrt(i); // no compilation error
Pero, ¿es eso realmente una solución?
Tenga en cuenta que la raíz cuadrada de
47
es
6.8556546004
... pero
squareRoot
obtendrá el valor
6
.
(La conversión se truncará, no se redondeará).
¿Y qué hay de esto?
byte b = (int) 512;
Eso da como resultado que
b
obtenga el valor
0
.
La conversión de un tipo int más grande a un tipo int más pequeño se realiza al enmascarar los bits de orden superior, y los 8 bits de orden inferior de
512
son todos cero.
En resumen, no debería simplemente agregar un encasillamiento, ya que podría no hacer lo correcto para su aplicación .
En su lugar, debe comprender por qué su código necesita hacer una conversión:
- ¿Ocurre esto porque ha cometido algún otro error en su código?
-
¿Debería
<type1>
ser un tipo diferente, para que no se necesite una conversión con pérdida aquí? - Si es necesaria una conversión, ¿es la conversión con pérdida silenciosa que el encasillado realizará el comportamiento correcto?
- ¿O debería hacer su código algunas verificaciones de rango y tratar con valores incorrectos / inesperados lanzando una excepción?
"Posible conversión con pérdida" al subscribir.
Primer ejemplo:
for (double d = 0; d < 10.0; d += 1.0) {
System.out.println(array[d]); // <<-- possible lossy conversion
}
El problema aquí es que el valor del índice de matriz debe ser
int
.
Entonces
d
tiene que ser convertido de
double
a
int
.
En general, usar un valor de punto flotante como un índice no tiene sentido.
O alguien tiene la impresión de que los arreglos de Java funcionan como (por ejemplo) los diccionarios de Python, o han pasado por alto el hecho de que la aritmética de punto flotante a menudo es inexacta.
La solución es volver a escribir el código para evitar el uso de un valor de punto flotante como un índice de matriz. (Agregar un tipo de conversión es probablemente una solución incorrecta).
Segundo ejemplo:
for (long l = 0; l < 10; l++) {
System.out.println(array[l]); // <<-- possible lossy conversion
}
Esta es una variación del problema anterior, y la solución es la misma. La diferencia es que la causa raíz es que las matrices de Java están limitadas a índices de 32 bits. Si desea una estructura de datos "tipo matriz" que tenga más de 2 31 - 1 elementos, necesita definir o encontrar una clase para hacerlo.
"Posible conversión con pérdida" con literales.
Considera esto:
int a = 21; byte b1 = a; // << - posible byte de conversión con pérdida b2 = 21; // OKAY
Que esta pasando? ¿Por qué se permite una versión pero la otra no? (Después de todo, ellos "hacen" lo mismo!)
En primer lugar, el JLS establece que
21
es un literal numérico cuyo tipo es
int
.
(No hay
byte
ni literales
short
). Por lo tanto, en ambos casos estamos asignando un
int
a un
byte
.
En el primer caso, el motivo del error es que no todos los valores
int
cabrán en un
byte
.
En el segundo caso, el compilador sabe que
21
es un valor que siempre encajará en un
byte
.
La explicación técnica es que, en un
contexto de asignación
, es permisible realizar una * reducción primitiva de conversión
to a
byte
,
char
or
short
if: - the value is the result of a compile time *constant expression* (which includes literals), and - the type of the expression is
byte
,
short
,
char
or
int`, y - el valor es representable (sin pérdida) en el tipo "destino".
Tenga en cuenta que esto solo se aplica con las declaraciones de asignación. Así:
Byte b4 = nuevo byte (21); // incorrecto
da un error de compilación, aunque el compilador no lo describirá como una "posible conversión con pérdida". (Se dirá que no hay un constructor que coincida).
1 - Por ejemplo, el IDE de Eclipse tiene una opción que le permite ignorar los errores de compilación y ejecutar el código de todos modos.
Si selecciona esto, el compilador del IDE creará un archivo
.class
donde el método con el error lanzará una excepción no marcada si se llama.
El mensaje de excepción mencionará el mensaje de error de compilación.