java compiler-errors

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.

  1. Una conversión de un long a un int es una conversión con pérdida potencial porque hay valores long que no tienen un valor int correspondiente. Por ejemplo, cualquier valor long que sea mayor que 2 ^ 31 - 1 es demasiado grande para ser representado como un int . Del mismo modo, cualquier número menor que -2 ^ 31 es demasiado pequeño.

  2. Una conversión de un int a un long NO es una conversión con pérdida porque cada valor int tiene un valor long correspondiente.

  3. Una conversión de un float a un long es una conversión con pérdida potencial porque existen valores float que son demasiado grandes o demasiado pequeños para representar valores tan long .

  4. Una conversión de un valor long a un valor float NO es una conversión con pérdida porque cada valor long tiene un valor float 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 a byte o char
  • char a byte o short
  • int byte , short o char
  • long a byte , short , char o int
  • float a byte , short , char , int o long
  • double a byte , short , char , int , long o float .

¿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.