Какие проблемы могут возникнуть при приведении типов long и Integer в Java
Что проверяют
Понимаете ли вы разницу между примитивами и обёртками и правила автоупаковки, а не только то, что long шире int. Следом нередко показывают код с Map<Long, ...> и спрашивают, почему get возвращает null.
Как отвечать
Разделите проблемы на примитивные и объектные. С примитивами:
- сужение:
(int) someLongмолча отбрасывает старшие биты, и3_000_000_000Lпревращается в отрицательное число. Если переполнение недопустимо, используйтеMath.toIntExact(), он броситArithmeticException; - переполнение до расширения: в
long ms = days * 24 * 60 * 60 * 1000приint daysумножение идёт вintи переполняется до присваивания. Лечится литералом1000Lв начале выражения.
С обёртками:
Integerне приводится кLong:(Long) obj, где внутриInteger, даётClassCastException, аLong x = someIntegerне компилируется. Частый источник: Jackson кладёт числа вMap<String, Object>какIntegerилиLongв зависимости от величины;equalsучитывает тип:Long.valueOf(1).equals(1)возвращаетfalse. Поэтомуmap.get(1)наMap<Long, V>вернётnull:getпринимаетObject, аргумент упаковался вInteger, компилятор молчит;==на обёртках: сравнивает ссылки. Для значений от-128до127работает кэш, и тест проходит, а на 128 уже нет;- распаковка
null:long x = integerValueприnullдаётNullPointerException. Особенно коварно в тернарном операторе, где один операндInteger, а другой примитив: распаковка там неявная.
Надёжный подход: приводить к одному типу явно через longValue(), сравнивать через equals или Objects.equals и держать ID одного типа по всему коду, от сущности до ключей коллекций.
Где спотыкаются
Утверждают, что Integer нельзя присвоить long без явного приведения. Можно: срабатывает распаковка и затем расширение, именно поэтому и возможен NPE.
Сравнивают Long через ==, потому что «в тестах работало» на маленьких ID.
Используют (int) вместо Math.toIntExact() и получают отрицательные значения без единой ошибки в логах.