同僚がBigDecimalの扱いでハマったのでメモ。
イミュータブル(Immutable)とは
変数の値オブジェクトを操作するとき、その変数オブジェクト自身の値を書き換えれることを「ミュータブル」、書き換えれないことを「イミュータブル」といいます。Javaの場合、intやdoubleCalendarはミュータブル、Stringはイミュータブルになります。
BigDecimalはどっち?
BigDecimalのAPIドキュメント
https://docs.oracle.com/javase/jp/8/docs/api/java/math/BigDecimal.html
足し算をする add メソッドについて見てみましょう。
/**
値が(this+augend)でスケールがmax(this.scale(), augend.scale())であるBigDecimalを返します。
パラメータ: augend - このBigDecimalに加算する値。
戻り値: this + augend
*/
public BigDecimal add(BigDecimal augend)
さて、次のコードの出力結果はいくつになるでしょう?
public static void main(String args[]) {
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("2");
a.add(b);
System.out.println(a);
}
出力結果。
1
あれ?3じゃないの?と思ったあなた。APIドキュメントの大事な一文を見落としていますよ。
変更が不可能な、任意精度の符号付き10進数です。
つまり、このコードは、a+b の値を持つBigDecimalのオブジェクトを新たに作って、そのオブジェクトを返すという意味だったのです。a自身の値は一切変更されないのです。
a.add(b);
DBから数値をBigDecimalで取り出してお金の計算をするコードだったのだけれど、addなどのメソッドの結果を変数に代入せず処理を進めて、物の見事にバグっていました。
コードの字面だけではイミュータブルなことを見抜けない……なんとも厄介なバグなのでした。