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

Инициализация с примитивами

Поскольку BigDecimal неизменяем, вам не нужен уникальный экземпляр каждого значения, вы должны взять существующий экземпляр желаемого значения из кеша, когда это применимо (кешированные значения от 0 до 10), используя метод valueOf.

// Assigns a cached instance (Object Reference)
BigDecimal valueA = BigDecimal.valueOf(8);

// Assigns a new instance (Object Reference)
BigDecimal valueB = new BigDecimal(8);
// Assigns a new instance (Object Reference)
BigDecimal valueA = BigDecimal.valueOf(11);

// Assigns a new instance (Object Reference)
BigDecimal valueB = new BigDecimal(11);

Этот метод также полезен для преобразования типов double и float в объекты BigDecimal. Вы должны использовать valueOf, чтобы обеспечить точное представление присвоенного значения ( совпадает с отображаемым значением при печати числа double/float).

// Assigned value: 20.51
BigDecimal valueA = BigDecimal.valueOf(20.51);

// Assigned value: 20.51
BigDecimal valueB = BigDecimal.valueOf(20.5100);

// Assigned value: 20.510000000001124002 <Unpredictable precision>
BigDecimal valueC = new BigDecimal(20.51);

valueOf следует использовать вместо конструкторов, когда значение исходит из примитивного типа данных.

Инициализация со строками

Когда значение, которое нужно преобразовать в BigDecimal, является String. Лучший способ создать новый экземпляр класса BigDecimal — вызвать параметризованный конструктор с аргументом String. В отличие от предыдущего раздела, этот конструктор идеально устанавливает значение, избегая проблем с точностью, таких как числа с плавающей запятой (double/float).

// Bad - Converting String into double/float before BigDecimal
// Value 0.65000000222004 <Unpredictable precision>
BigDecimal valueA = new BigDecimal(Double.valueOf("0.65"));

// Good - Converting String into BigDecimal using constructor
// Value 0.65
BigDecimal valueB = new BigDecimal("0.65");

Конструктор следует использовать, когда значение поступает из String, чтобы обеспечить точность.

Обратите внимание, что всякий раз, когда контроль над типами данных находится в ваших руках, вам следует работать с примитивными типами данных с помощью valueOf, так как это более эффективно, чем использование конструктора с String потому что значение присваивается сразу, а не анализируется заранее.

Сравнение значений

Если вы не хотите сравнивать адреса памяти, избегайте оператора ==.
Метод equals не является хорошим способом сравнения значений, этот метод сравнивает значения и масштабы, поэтому без шага стандартизации для масштабов перед сравнением BigDecimals, equals в конечном итоге вызовут некоторые проблемы.
Таким образом, лучший способ сравнить значения — это использовать метод compareTo.

// Using == operator
BigDecimal valueA = BigDecimal.valueOf(22);
BigDecimal valueB = BigDecimal.valueOf(22.0);
valueA == valueB; // ← false (Comparing memory addresses)
// Using equals method
BigDecimal valueA = BigDecimal.valueOf(22); // value: 22 / scale: 0
BigDecimal valueB = BigDecimal.valueOf(22.0); // value: 22 / scale: 1
valueA.equals(valueB); // ← false (Comparing value and scale)
// Using compareTo method
BigDecimal valueA = BigDecimal.valueOf(22); // value: 22 / scale: 0
BigDecimal valueB = BigDecimal.valueOf(22.0); // value: 22 / scale: 1
valueA.compareTo(valueB) == 0; // ← true (Comparing value)

compareTo — это правильный способ сравнения значений вместо оператора == или метода equals.

BigDecimal неизменяем

Самая распространенная ошибка при работе с BigDecimals заключается в том, что иногда мы забываем об неизменности этих экземпляров. Наверное, вы не раз видели эти кейсы:

// Increase the value from 15.5 to 16.6
BigDecimal valueA = BigDecimal.valueOf(15.5);

// Bad - valueA does not change its value
valueA.add(BigDecimal.ONE); // new instance is not assigned

// Good - valueA is reassigned
valueA = valueA.add(BigDecimal.ONE); // new instance is assigned
// Increase the scale: from 12.9 to 12.90
BigDecimal valueA = BigDecimal.valueOf(12.9);

// Bad - valueA does not change its scale
valueA.setScale(2); // new instance is not assigned

// Good - valueA is reassigned
valueA = valueA.setScale(2); // new instance is assigned
// Negate the value from 55.1 to -55.1
BigDecimal valueA = BigDecimal.valueOf(55.1);

// Bad - valueA does not change its value
valueA.negate(); // new instance is not assigned

// Good - valueA is reassigned
valueA = valueA.negate(); // new instance is assigned

Неизменяемый экземпляр нельзя изменить.

Выводы

Я надеюсь, что вы найдете эти советы полезными для ваших следующих задач кодирования или проверки кода. В моем повседневном опыте работы в качестве бэкенд-разработчика Java я нахожу интересные темы, подобные этой, и я продолжу делиться ими в следующих статьях.