Conversión segura larga a int en Java

¿Cuál es la forma más idiomática en Java para verificar que un elenco desde long a int no pierda ninguna información?

Esta es mi implementación actual:

 public static int safeLongToInt(long l) { int i = (int)l; if ((long)i != l) { throw new IllegalArgumentException(l + " cannot be cast to int without changing its value."); } return i; } 

Se ha agregado un nuevo método con Java 8 para hacer justamente eso.

 import static java.lang.Math.toIntExact; long foo = 10L; int bar = toIntExact(foo); 

Lanzará una ArithmeticException en caso de desbordamiento.

Ver: Math.toIntExact(long)

Varios otros métodos seguros de desbordamiento se han agregado a Java 8. Terminan con exacto .

Ejemplos:

  • Math.incrementExact(long)
  • Math.subtractExact(long, long)
  • Math.decrementExact(long)
  • Math.negateExact(long),
  • Math.subtractExact(int, int)

Creo que lo haría tan simple como:

 public static int safeLongToInt(long l) { if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) { throw new IllegalArgumentException (l + " cannot be cast to int without changing its value."); } return (int) l; } 

Creo que expresa la intención más claramente que el casting repetido … pero es algo subjetivo.

Nota de interés potencial: en C # solo sería:

 return checked ((int) l); 

Con la clase Ints de Google Guava, su método se puede cambiar a:

 public static int safeLongToInt(long l) { return Ints.checkedCast(l); } 

De los documentos vinculados:

revisado

public static int checkedCast(long value)

Devuelve el valor int que es igual a value , si es posible.

Parámetros: value – cualquier valor en el rango del tipo int

Devuelve: el valor int que equivale al value

Lanza: IllegalArgumentException – si el value es mayor que Integer.MAX_VALUE o menor que Integer.MIN_VALUE

Por cierto, no necesitas el contenedor safeLongToInt , a menos que quieras dejarlo en su lugar para cambiar la funcionalidad sin una extensa refactorización, por supuesto.

Con BigDecimal:

 long aLong = ...; int anInt = new BigDecimal(aLong).intValueExact(); // throws ArithmeticException // if outside bounds 

aquí hay una solución, en caso de que no le importe el valor en caso de que sea más grande de lo que se necesita;)

 public static int safeLongToInt(long l) { return (int) Math.max(Math.min(Integer.MAX_VALUE, l), Integer.MIN_VALUE); } 

NO: ¡Esto no es una solución!

Mi primer acercamiento fue:

 public int longToInt(long theLongOne) { return Long.valueOf(theLongOne).intValue(); } 

Pero eso simplemente arroja el largo a un int, creando potencialmente nuevas instancias Long o recuperándolas del grupo Long.


Los inconvenientes

  1. Long.valueOf crea una nueva instancia Long si el número no está dentro del rango del grupo Long [-128, 127].

  2. La implementación de intValue no hace más que:

     return (int)value; 

Así que esto se puede considerar incluso peor que simplemente lanzar el long a int .

Yo afirmo que la forma obvia de ver si el lanzamiento de un valor cambió el valor sería emitir y verificar el resultado. Sin embargo, eliminaría el yeso innecesario al comparar. Tampoco me gustan mucho los nombres de variables de una letra (excepción y , pero no cuando se trata de filas y columnas (a veces, respectivamente)).

 public static int intValue(long value) { int valueInt = (int)value; if (valueInt != value) { throw new IllegalArgumentException( "The long value "+value+" is not within range of the int type" ); } return valueInt; } 

Sin embargo, realmente me gustaría evitar esta conversión si es posible. Obviamente, a veces no es posible, pero en esos casos IllegalArgumentException es casi seguro la excepción equivocada en lo que respecta al código del cliente.

Los tipos enteros de Java se representan como firmados. Con una entrada entre 2 31 y 2 32 (o -2 31 y -2 32 ), el elenco tendría éxito pero su prueba fallaría.

Lo que se debe verificar es si todos los bits altos del long son todos iguales:

 public static final long LONG_HIGH_BITS = 0xFFFFFFFF80000000L; public static int safeLongToInt(long l) { if ((l & LONG_HIGH_BITS) == 0 || (l & LONG_HIGH_BITS) == LONG_HIGH_BITS) { return (int) l; } else { throw new IllegalArgumentException("..."); } } 
 (int) (longType + 0) 

pero Long no puede exceder el máximo 🙂

Otra solución puede ser:

 public int longToInt(Long longVariable) { try { return Integer.valueOf(longVariable.toString()); } catch(IllegalArgumentException e) { Log.e(e.printstackstrace()); } } 

Lo he intentado para casos en los que el cliente está realizando una POST y la base de datos del servidor solo entiende números enteros mientras que el cliente tiene una extensión larga.