Quelle est la manière la plus idiomatique de Java de vérifier qu'un transtypage de long
à int
ne perd aucune information?
Ceci est ma mise en œuvre actuelle:
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;
}
Une nouvelle méthode a été ajoutée avec Java 8 pour faire exactement cela.
import static Java.lang.Math.toIntExact;
long foo = 10L;
int bar = toIntExact(foo);
Jetera un ArithmeticException
en cas de débordement.
Voir: Math.toIntExact(long)
Plusieurs autres méthodes protégées contre les débordements ont été ajoutées à Java 8. Elles se terminent par exact .
Exemples:
Math.incrementExact(long)
Math.subtractExact(long, long)
Math.decrementExact(long)
Math.negateExact(long),
Math.subtractExact(int, int)
Je pense que je le ferais aussi simplement que:
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;
}
Je pense que cela exprime l'intention plus clairement que le casting répété ... mais c'est un peu subjectif.
Note d'intérêt potentiel - en C #, ce serait simplement:
return checked ((int) l);
Avec la classe Ints de Google Guava, votre méthode peut être modifiée en:
public static int safeLongToInt(long l) {
return Ints.checkedCast(l);
}
À partir des documents liés:
vérifiéCast
public static int checkedCast(long value)
Renvoie la valeur int égale à
value
, si possible.Paramètres:
value
- toute valeur comprise dans la plage du typeint
Retourne: la valeur
int
égale àvalue
Lance:
IllegalArgumentException
- sivalue
est supérieur àInteger.MAX_VALUE
ou inférieur àInteger.MIN_VALUE
Incidemment, vous n’avez pas besoin de l’encapsuleur safeLongToInt
, sauf si vous souhaitez le laisser en place pour pouvoir modifier la fonctionnalité sans refactorisation complète, bien sûr.
Avec BigDecimal:
long aLong = ...;
int anInt = new BigDecimal(aLong).intValueExact(); // throws ArithmeticException
// if outside bounds
voici une solution, au cas où vous ne vous souciez pas de la valeur si elle est plus grande que nécessaire;)
public static int safeLongToInt(long l) {
return (int) Math.max(Math.min(Integer.MAX_VALUE, l), Integer.MIN_VALUE);
}
DONT: Ce n'est pas une solution!
Ma première approche a été:
public int longToInt(long theLongOne) {
return Long.valueOf(theLongOne).intValue();
}
Mais cela ne fait que transformer le long en un entier, créant potentiellement de nouvelles instances Long
ou les récupérant du pool Long.
Les inconvénients
Long.valueOf
crée une nouvelle instance Long
si le nombre n'est pas compris dans la plage de pool de Long
[-128, 127].
L'implémentation intValue
ne fait rien d'autre que:
return (int)value;
Donc, cela peut être considéré comme pire que de simplement lancer le long
à int
.
Je prétends que la manière évidente de voir si le fait de changer une valeur a changé la valeur serait de lancer et de vérifier le résultat. Je voudrais cependant supprimer le casting inutile lors de la comparaison. Je n’apprécie pas trop les noms de variables à une lettre (exception x
et y
, mais pas quand ils désignent ligne et colonne (parfois respectivement)).
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;
}
Cependant, je voudrais vraiment éviter cette conversion si possible. Bien sûr, parfois, ce n'est pas possible, mais dans ces cas, IllegalArgumentException
est presque certainement la mauvaise exception à invoquer en ce qui concerne le code client.
Les types entiers Java sont représentés comme signés. Avec une entrée entre 231 et 232 (ou -231 et -232) la distribution réussirait mais votre test échouerait.
Ce qu'il faut vérifier, c'est si tous les bits hauts de la long
sont tous identiques:
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)
mais Long ne peut pas dépasser le maximum :)