Безопасное приведение long к int в Java
Какой самый идиоматичный способ в 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 ответ(ов)
Я бы сделал это так:
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);
При использовании класса 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()
(который не бросает исключение, но может привести к потере данных).
Вот решение, если вам не важен факт, что значение может превышать допустимый диапазон:
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
.
НЕ ДЕЛАЙТЕ: Это не решение!
Мой первый подход был следующим:
public int longToInt(long theLongOne) {
return Long.valueOf(theLongOne).intValue();
}
Но это всего лишь приводит к приведению long
к int
, потенциально создавая новые экземпляры Long
или извлекая их из пула Long
.
Недостатки
Long.valueOf
создает новый экземплярLong
, если число не входит в диапазон пулаLong
[-128, 127].Реализация
intValue
не делает ничего более, чем:return (int)value;
Поэтому это можно считать даже худшим вариантом, чем просто приведение long
к int
.
Ваше утверждение о том, что очевидный способ проверить, изменилась ли величина при приведении типа, заключается в том, чтобы привести значение и проверить результат. Тем не менее, я бы убрал ненужное приведение при сравнении. Также мне не очень нравятся однобуквенные имена переменных (исключая, возможно, 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; // Приведение здесь безопасно
}
Таким образом, вы минимизируете ненужные приведения и скорректируете тип исключений в зависимости от ситуации.
Почему операторы присваивания сCompound типа в Java (+=, -=, *=, /=) не требуют приведения типов?
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?
Как объявить массив в одну строку?
Загрузка JDK Java на Linux через wget приводит к отображению страницы лицензии вместо установки
Создание репозитория Spring без сущности