Статьи

Точное преобразование Long в Int в Java

Со всеми блестящими вещами ( лямбда-выражениями , потоками , Optional , новым API Date / Time и т. Д.), Чтобы отвлечь мое внимание, которое пришло с JDK 8 , я не обращал особого внимания на добавление метода Math.toIntExact () , Тем не менее, это небольшое дополнение может быть довольно полезным само по себе.

Документация Javadoc для Math.toIntExact (long) гласит: «Возвращает значение long аргумента; выбрасывая исключение, если значение переполняет int . » Это особенно полезно в ситуациях, когда человеку дается или уже есть Long и ему нужно вызвать API, который ожидает int . Конечно, было бы лучше, если бы API могли быть изменены для использования одного и того же типа данных, но иногда это не поддается контролю. Когда нужно ввести Long в int существует вероятность переполнения целым числом, потому что числовое значение Long может иметь большую величину, чем может точно представить int .

Если говорят, что данный Long никогда не будет больше, чем может хранить int , статический метод Math.toIntExact(Long) особенно полезен, потому что он вызовет непроверенную ArithmeticException, если возникнет такая «исключительная» ситуация, и станет очевидным, что произошла «исключительная» ситуация.

Когда Long.intValue () используется для получения целого числа из Long , исключение не выдается, если происходит целочисленное переполнение. Вместо этого предоставляется целое число, но это значение редко будет полезным из-за переполнения целого числа. Почти в каждом возможном случае лучше встретить исключение времени выполнения, которое предупреждает о целочисленном переполнении, чем если программное обеспечение продолжит неправильно использовать номер переполнения.

В качестве первого шага в иллюстрации различий между Long.intValue() и Math.toIntExact(Long) следующий код генерирует диапазон значений Long от 5 меньше, чем Integer.MAX_VALUE, до 5 больше, чем Integer.MAX_VALUE .

Диапазон генерирования Long s, который включает в себя Integer.MAX_VALUE

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/**
 * Generate {@code Long}s from range of integers that start
 * before {@code Integer.MAX_VALUE} and end after that
 * maximum integer value.
 *
 * @return {@code Long}s generated over range includes
 *    {@code Integer.MAX_VALUE}.
 */
public static List<Long> generateLongInts()
{
   final Long maximumIntegerAsLong = Long.valueOf(Integer.MAX_VALUE);
   final Long startingLong = maximumIntegerAsLong - 5;
   final Long endingLong = maximumIntegerAsLong + 5;
   return LongStream.range(startingLong, endingLong).boxed().collect(Collectors.toList());
}

В следующем листинге кода показаны два метода, которые демонстрируют два ранее упомянутых подхода для получения int из Long .

Использование Long.intValue() и Math.toIntExact(Long)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
 * Provides the {@code int} representation of the provided
 * {@code Long} based on an invocation of the provided
 * {@code Long} object's {@code intValue()} method.
 *
 * @param longRepresentation {@code Long} for which {@code int}
 *    value extracted with {@code intValue()} will be returned.
 * @return {@code int} value corresponding to the provided
 *    {@code Long} as provided by invoking the method
 *    {@code intValue()} on that provided {@code Long}.
 * @throws NullPointerException Thrown if the provided long
 *    representation is {@code null}.
 */
public static void writeLongIntValue(final Long longRepresentation)
{
   out.print(longRepresentation + " =>       Long.intValue() = ");
   try
   {
      out.println(longRepresentation.intValue());
   }
   catch (Exception exception)
   {
      out.println("ERROR - " + exception);
   }
}
 
/**
 * Provides the {@code int} representation of the provided
 * {@code Long} based on an invocation of {@code Math.toIntExact(Long)}
 * on the provided {@code Long}.
 *
 * @param longRepresentation {@code Long} for which {@code int}
 *    value extracted with {@code Math.toIntExact(Long)} will be
 *    returned.
 * @return {@code int} value corresponding to the provided
 *    {@code Long} as provided by invoking the method
 *    {@code Math.toIntExact)Long} on that provided {@code Long}.
 * @throws NullPointerException Thrown if the provided long
 *    representation is {@code null}.
 * @throws ArithmeticException Thrown if the provided {@code Long}
 *    cannot be represented as an integer without overflow.
 */
public static void writeIntExact(final Long longRepresentation)
{
   out.print(longRepresentation + " => Math.toIntExact(Long) = ");
   try
   {
      out.println(Math.toIntExact(longRepresentation));
   }
   catch (Exception exception)
   {
      out.println("ERROR: " + exception);
   }
}

Когда приведенный выше код выполняется с диапазоном Long s, созданным в более раннем листинге кода ( полный код доступен на GitHub ), вывод будет выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
2147483642 =>       Long.intValue() = 2147483642
2147483642 => Math.toIntExact(Long) = 2147483642
2147483643 =>       Long.intValue() = 2147483643
2147483643 => Math.toIntExact(Long) = 2147483643
2147483644 =>       Long.intValue() = 2147483644
2147483644 => Math.toIntExact(Long) = 2147483644
2147483645 =>       Long.intValue() = 2147483645
2147483645 => Math.toIntExact(Long) = 2147483645
2147483646 =>       Long.intValue() = 2147483646
2147483646 => Math.toIntExact(Long) = 2147483646
2147483647 =>       Long.intValue() = 2147483647
2147483647 => Math.toIntExact(Long) = 2147483647
2147483648 =>       Long.intValue() = -2147483648
2147483648 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow
2147483649 =>       Long.intValue() = -2147483647
2147483649 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow
2147483650 =>       Long.intValue() = -2147483646
2147483650 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow
2147483651 =>       Long.intValue() = -2147483645
2147483651 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow

Integer.MAX_VALUE строки указывают код, обрабатывающий Long со значением, равным Integer.MAX_VALUE . После этого отображается Long представляющий единицу больше, чем Integer.MAX_VALUE с результатами попытки преобразования этого Long в int с использованием Long.intValue() и Math.toIntExact(Long) . Long.intValue() встречает целочисленное переполнение, но не вызывает исключение и вместо этого возвращает отрицательное число -2147483648 . Метод Math.toIntExact(Long) не возвращает значение при переполнении целого числа, а вместо этого выдает Math.toIntExact(Long) ArithmeticException с информативным сообщением «переполнение целого числа».

Метод Math.toIntExact(Long) не так важен, как многие из функций, представленных в JDK 8, но он может быть полезен во избежание ошибок, связанных с целочисленным переполнением, которые иногда бывает сложно диагностировать.

Опубликовано на Java Code Geeks с разрешения Дастина Маркса, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Точное преобразование Long в Int в Java

Мнения, высказанные участниками Java Code Geeks, являются их собственными.