Безопасное приведение к 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 экземпляры или извлечение их из длинного пула.


Недостатки

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

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

но долго не может превышать максимум:)

Еще одно решение может быть:

public int longToInt(Long longVariable)
{
    try { 
            return Integer.valueOf(longVariable.toString()); 
        } catch(IllegalArgumentException e) { 
               Log.e(e.printstackstrace()); 
        }
}

Я пробовал это для случаев, когда клиент делает POST, а серверная БД понимает только целые числа, в то время как у клиента есть Long.

Другие вопросы по тегам