Безопасное приведение к int в Java
Какой самый идиоматический способ в Java проверить, что приведение long
в int
не теряет никакой информации?
Это моя текущая реализация:
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;
}
10 ответов
Новый метод был добавлен в Java 8, чтобы сделать это.
import static java.lang.Math.toIntExact;
long foo = 10L;
int bar = toIntExact(foo);
Бросит ArithmeticException
в случае переполнения.
Увидеть: Math.toIntExact(long)
Несколько других безопасных методов переполнения были добавлены в Java 8. Они заканчиваются точным.
Примеры:
Math.incrementExact(long)
Math.subtractExact(long, long)
Math.decrementExact(long)
Math.negateExact(long),
Math.subtractExact(int, int)
Я думаю, я бы сделал это так же просто, как:
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;
}
Я думаю, что это выражает намерение более четко, чем повторное приведение... но это несколько субъективно.
Примечание потенциального интереса - в C# это будет просто:
return checked ((int) l);
С помощью класса Ints Google Guava ваш метод может быть изменен на:
public static int safeLongToInt(long l) {
return Ints.checkedCast(l);
}
Из связанных документов:
checkedCast
public static int checkedCast(long value)
Возвращает значение типа int, равное
value
, если возможно.Параметры:
value
- любое значение в диапазонеint
типВозвращает:
int
значение, равноеvalue
Броски:
IllegalArgumentException
- еслиvalue
больше, чемInteger.MAX_VALUE
или меньше чемInteger.MIN_VALUE
Кстати, вам не нужно safeLongToInt
обертка, если вы не хотите оставить ее на месте для изменения функциональности без обширных рефакторинга, конечно.
С BigDecimal:
long aLong = ...;
int anInt = new BigDecimal(aLong).intValueExact(); // throws ArithmeticException
// if outside bounds
Вот решение, в случае, если вы не заботитесь о стоимости, если она больше, чем нужно;)
public static int safeLongToInt(long l) {
return (int) Math.max(Math.min(Integer.MAX_VALUE, l), Integer.MIN_VALUE);
}
НЕ: Это не решение!
Мой первый подход был:
public int longToInt(long theLongOne) {
return Long.valueOf(theLongOne).intValue();
}
Но это просто сводит длинное к целому, потенциально создавая новые 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
почти наверняка это неправильное исключение, которое будет выброшено в отношении клиентского кода.
Целочисленные типы Java представлены как подписанные. При вводе от 231 до 232 (или от -231 до -232) приведение будет успешным, но ваш тест не пройден.
Что проверить, все ли старшие биты long
все одинаковы
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("...");
}
}
Еще одно решение может быть:
public int longToInt(Long longVariable)
{
try {
return Integer.valueOf(longVariable.toString());
} catch(IllegalArgumentException e) {
Log.e(e.printstackstrace());
}
}
Я пробовал это для случаев, когда клиент делает POST, а серверная БД понимает только целые числа, в то время как у клиента есть Long.