web-dev-qa-db-fra.com

Casting en toute sécurité long pour int dans Java

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

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)
551
Pierre-Antoine

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);
303
Jon Skeet

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 type int

Retourne: la valeur int égale à value

Lance: IllegalArgumentException - si value 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.

129
prasopes

Avec BigDecimal:

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

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);
}
17
Vitaliy Kulikov

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

  1. Long.valueOf crée une nouvelle instance Long si le nombre n'est pas compris dans la plage de pool de Long [-128, 127].

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

12
Andreas

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.

7

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("...");
    }
}
2
mob
(int) (longType + 0)

mais Long ne peut pas dépasser le maximum :)

0
Maury