Вы, наверное, уже знаете, что оператор Java «==» довольно сложен. Потому что это просто бесполезно в большинстве случаев. В Java вы имеете дело в основном с объектами, и вы не можете использовать оператор «==» для сравнения объектов, вместо этого вы должны использовать метод .equals(). Что касается объекта «==», сравнивайте ссылки на объекты, а не их фактическое содержимое.

Несмотря на это, можно увидеть такой код, который не только использует «==» для сравнения объектов, но и работает корректно. Некоторые части этого кода, обнаруженные в продакшене, можно считать исключительным примером программистской тупости, иначе говоря, шиткод.

Вот наше тестовое приложение. И вы обязательно заметите, какой у него странный вид.

import static java.lang.System.out;
import java.math.BigDecimal;
public class Main
{
    public static void main(String[] args) {
        // Note that our variables are objects
        Integer a = 1;
        Integer b = 666;
        out.println(a == 1);
        out.println(b == 666);
        out.println(b == new Integer(666));
        out.println(a == new Integer(1));
        out.println(new Integer(1) == new Integer(1));
        out.println(a == Integer.valueOf(1));
        out.println(b == Integer.valueOf(666));
        out.println(Integer.valueOf(666) == Integer.valueOf(666));
        BigDecimal bd = BigDecimal.valueOf(33.0);
        out.println(bd.equals(new BigDecimal("33.0")));
        out.println(bd.equals(new BigDecimal("33.00")));
        out.println(bd.equals(new BigDecimal("33")));
        out.println(bd.equals(new BigDecimal(33)));
    }
}

Давайте углубимся в результаты построчно.

a == 1; // -> true
b == 666; // -> true

У нас есть объект с одной стороны и примитив с другой. Мы не можем сравнивать напрямую, поэтому Java выполняет распаковку — преобразует объект в примитив. И можно сравнивать примитивы с помощью оператора «==».

b == new Integer(600+60+6); // -> false
a == new Integer(1); // -> false
new Integer(1) == new Integer(1) // -> false

Теперь у нас есть объекты по обе стороны от нашего выражения, и они различны независимо от значений, которые они содержат. Все еще выглядит нормально.

a == Integer.valueOf(1); // -> true
Integer.valueOf(1) == Integer.valueOf(1); // -> true
b == Integer.valueOf(666); // -> false
Integer.valueOf(666) == Integer.valueOf(666) // -> false

Вот где все становится сложно. И что, черт возьми, происходит с методом valueOf? Дело в том, что поведение valueOf похоже на автобоксинг. Это означает, что Integer a = 1; совпадает с Integer a = Integer.valueOf(1);.

Но почему некоторые значения автоупаковки сопоставимы через «==», а другие нет. Это связано с кэшированием значений объекта Integer от -128 до 127 (включительно). Таким образом, он всегда будет возвращать один и тот же объект (с одной и той же ссылкой) для кэшированных значений. Значение 666 выходит за пределы диапазона кэширования, поэтому каждый раз будет создаваться новый объект.

Ну так если мы будем сравнивать объекты с equals, то все должно быть в порядке.

a.equals(1); // -> true
b.equals(666); // -> true

Это верно для большинства случаев, но у нашего паршивого Java API есть одна маленькая ловушка.

BigDecimal bd = BigDecimal.valueOf(33.0);
bd.equals(new BigDecimal("33.0")); // -> true
bd.equals(new BigDecimal("33.00")); // -> false
bd.equals(new BigDecimal("33")); // -> false
bd.equals(new BigDecimal(33)); // -> false

В вашем лице программисты :) Получается, что с точки зрения BigDecimal, если представление «33.0» и «33.00» — это разные значения, потому что они имеют разную точность. Неважно, содержат ли объекты BigDecimal одно и то же значение. Если бы не точность, они не могли бы быть равными.

Итак, как мы должны сравнивать BigDecimals?

bd.compareTo(new BigDecimal("33.00")) == 0; // -> true
bd.compareTo(new BigDecimal("33")) == 0; // -> true
bd.compareTo(new BigDecimal(33)) == 0; // -> true

Вот так это работает, чуваки. Спасибо «умным» дебилам, которые поставили этот ино Java API.

Вещи, которые вы должны помнить:

  • Автоматическая распаковка в Java может быть сложной
  • Используйте примитивы везде, где только можно
  • equals метод является единственным способом для объектов
  • BigDecimal не является обычным объектом, используйте a.compareTo(b) == 0 для сравнения таких объектов.

Оригинальный URL статьи: https://mixeddev.info/articles/2020/04/11/java-equality-wtfs.html