Casting long long to int in Java

Qual è il modo più idiomatico in Java per verificare che un cast da long a int non perda alcuna informazione?

Questa è la mia attuale implementazione:

 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; } 

È stato aggiunto un nuovo metodo con Java 8 per fare proprio questo.

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

Lancia un ArithmeticException in caso di overflow.

Vedi: Math.toIntExact(long)

Diversi altri metodi sicuri di overflow sono stati aggiunti a Java 8. Si concludono con esatti .

Esempi:

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

Penso che lo farei semplicemente come:

 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; } 

Penso che esprima l’intento più chiaramente del casting ripetuto … ma è piuttosto soggettivo.

Nota di potenziale interesse – in C # sarebbe solo:

 return checked ((int) l); 

Con la class Ints di Google Guava, il tuo metodo può essere modificato in:

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

Dai documenti collegati:

checkedCast

public static int checkedCast(long value)

Restituisce il valore int che è uguale al value , se ansible.

Parametri: value – qualsiasi valore nell’intervallo del tipo int

Restituisce: il valore int uguale al value

IllegalArgumentException : IllegalArgumentException – se il value è maggiore di Integer.MAX_VALUE o inferiore a Integer.MIN_VALUE

Per inciso, non è necessario il wrapper safeLongToInt , a meno che non si voglia lasciarlo in posizione per modificare la funzionalità senza un ampio refactoring, naturalmente.

Con BigDecimal:

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

ecco una soluzione, nel caso in cui non ti interessa il valore nel caso in cui sia più grande di quanto necessario;)

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

NON: Questa non è una soluzione!

Il mio primo approccio è stato:

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

Ma questo non fa altro che lanciare il long ad un int, potenzialmente creando nuove istanze Long o recuperandole dal pool Long.


Gli svantaggi

  1. Long.valueOf crea una nuova istanza Long se il numero non rientra nell’intervallo di pool di Long [-128, 127].

  2. L’implementazione intValue non fa altro che:

     return (int)value; 

Quindi questo può essere considerato ancora peggiore del semplice casting di long a int .

Sostengo che il modo ovvio per vedere se trasmettere un valore ha cambiato il valore sarebbe quello di trasmettere e controllare il risultato. Tuttavia, rimuoverei il cast non necessario durante il confronto. Anch’io non amo troppo i nomi delle variabili di una lettera (eccezione y , ma non quando significano riga e colonna (a volte rispettivamente)).

 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; } 

Tuttavia, vorrei davvero evitare questa conversione se ansible. Ovviamente a volte non è ansible, ma in quei casi IllegalArgumentException è quasi certamente l’eccezione sbagliata da lanciare per quanto riguarda il codice client.

I tipi interi Java sono rappresentati come firmati. Con un input compreso tra 2 31 e 32 32 (o -2 31 e -2 32 ) il cast andrà a buon fine ma il test fallirà.

Che cosa controllare è se tutti i bit alti del long sono tutti uguali:

 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) 

ma Long non può superare il massimo 🙂

Un’altra soluzione può essere:

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

Ho provato questo per i casi in cui il client sta eseguendo un POST e il DB del server comprende solo Integers mentre il client ha Long.