5

Безопасное приведение long к int в Java

13

Какой самый идиоматичный способ в Java проверить, что преобразование из long в int не приведет к потере информации?

Вот моя текущая реализация:

public static int safeLongToInt(long l) {
    int i = (int)l;
    if ((long)i != l) {
        throw new IllegalArgumentException(l + " не может быть преобразован в int без изменения его значения.");
    }
    return i;
}

5 ответ(ов)

3

Я бы сделал это так:

public static int safeLongToInt(long l) {
    if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
        throw new IllegalArgumentException
            (l + " не может быть приведён к int без изменения его значения.");
    }
    return (int) l;
}

На мой взгляд, такой подход более ясно выражает намерение, чем многократное приведение типов... хотя это довольно субъективно.

Пожалуй, стоит отметить, что в C# для этого было бы достаточно написать:

return checked ((int) l);
0

При использовании класса BigDecimal, чтобы преобразовать long в int, можно использовать метод intValueExact(), который бросает исключение ArithmeticException, если значение находится за пределами диапазона типа int. Вот пример кода:

long aLong = ...;
int anInt = new BigDecimal(aLong).intValueExact(); // вызывает ArithmeticException
                                                    // если значение выходит за пределы

Это происходит потому, что int может хранить значения только в диапазоне от -2,147,483,648 до 2,147,483,647. Если ваше значение aLong превышает этот диапазон, метод intValueExact() выбросит исключение. Поэтому, если существует вероятность того, что значение может выйти за пределы этого диапазона, вам следует обработать это исключение или использовать метод intValue() (который не бросает исключение, но может привести к потере данных).

0

Вот решение, если вам не важен факт, что значение может превышать допустимый диапазон:

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

Этот метод работает следующим образом: он ограничивает значение l промежутком между Integer.MIN_VALUE и Integer.MAX_VALUE, после чего приводит его к типу int. Таким образом, если l выходит за пределы допустимых значений int, возвращается максимальное или минимальное значение, соответственно. Это позволяет избежать исключения ArithmeticException, которое может возникнуть при прямом приведении типа из long в int.

0

НЕ ДЕЛАЙТЕ: Это не решение!

Мой первый подход был следующим:

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

Но это всего лишь приводит к приведению long к int, потенциально создавая новые экземпляры Long или извлекая их из пула Long.


Недостатки

  1. Long.valueOf создает новый экземпляр Long, если число не входит в диапазон пула Long [-128, 127].

  2. Реализация intValue не делает ничего более, чем:

    return (int)value;
    

Поэтому это можно считать даже худшим вариантом, чем просто приведение long к int.

0

Ваше утверждение о том, что очевидный способ проверить, изменилась ли величина при приведении типа, заключается в том, чтобы привести значение и проверить результат. Тем не менее, я бы убрал ненужное приведение при сравнении. Также мне не очень нравятся однобуквенные имена переменных (исключая, возможно, x и y, но не в случае, когда они означают строку и столбец соответственно).

Код, который вы предоставили, имеет следующий вид:

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

Тем не менее, я все же хотел бы избежать этого преобразования, если это вообще возможно. Понятно, что иногда это невозможно, но в таких случаях IllegalArgumentException почти наверняка является неправильным исключением для броска с точки зрения клиентского кода. Лучше использовать специфическое исключение, такое как ArithmeticException, если значение выходит за пределы типа int.

Вот возможное улучшение вашего метода:

public static int intValue(long value) {
    if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
        throw new ArithmeticException(
            "The long value " + value + " is not within the range of the int type"
        );
    }
    return (int)value; // Приведение здесь безопасно
}

Таким образом, вы минимизируете ненужные приведения и скорректируете тип исключений в зависимости от ситуации.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь