Класс 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 я нахожу интересные темы, подобные этой, и я продолжу делиться ими в следующих статьях.