Java
Kotlin
BigDecimal
operator

kotlin_800x320.png

Kotlin を趣味で触り始めて数日ですが, Java の痒い所が丁寧にサポートされていて感動しています. BigDecimal 演算もその一つです.

業務アプリでは数値演算が必要になる事が多いですが, Double は使えたものではないので BigDecimal 一択です. そしてこの BigDecimal 演算は, 可読性が低いことこの上ありません.

コーディングスタイル比較

基本編

BigDecimalBasicSample.kt
/**
 * BigDecimal coding style : (a + b) / 2
 */
fun main(args: Array<String>) {
    val a = BigDecimal.ONE
    val b = BigDecimal.TEN

    // Java style
    println(a.add(b).divide(BigDecimal.valueOf(2L), RoundingMode.HALF_EVEN))

    // Kotlin basic style
    println((a + b) / 2.toBigDecimal())
}

Javaはひどい有様です.
Kotlinは四則演算の演算子を適用できるため, 非常に見やすくなりました. ただ 2.toBigDecimal() には依然不満が残ります.

応用編

BigDecimalCustomSample.kt
/** custome operator BigDecimal / Long */
operator fun BigDecimal.div(other : Long) : BigDecimal = this / other.toBigDecimal()

/**
 * BigDecimal coding style : (a + b) / 2
 */
fun main(args: Array<String>) {
    val a = BigDecimal.ONE
    val b = BigDecimal.TEN

    // Java style
    println(a.add(b).divide(BigDecimal.valueOf(2L), RoundingMode.UP))

    // Kotlin custom oeprator style
    println((a + b) / 2)
}

BigDecimal / Long の独自演算子を実装することで, 可読性がより上がりました.

仕組みについて

Kotlin の演算子の実現方法

Kotlin において演算子は, 決められたメソッドを呼び出すことと等価になります. Operator overloading において, 各演算子とそれに対応するメソッドが示されています. 四則演算子を抜粋すると以下になります.

Expression Translated to
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b), a.mod(b) (deprecated)
a..b a.rangeTo(b)

plusなどのメソッドを operator fun で実装することによって, 任意のクラス間の演算子を独自定義することができます.

BigDecimal に対する演算子の実装

kotlin-stdlib に含まれている BigDecimals.kt に演算子の実装が記述されています. サンプルとして除算 "/" の実装を抜粋します. BigDecimal#divide が利用されており, 更に引数に RoundingMode.HALF_EVEN が使われていることが確認できます.

BigDecimals.kt
/**
 * Enables the use of the `/` operator for [BigDecimal] instances.
 *
 * The scale of the result is the same as the scale of `this` (divident), and for rounding the [RoundingMode.HALF_EVEN]
 * rounding mode is used.
 */
@kotlin.internal.InlineOnly
public inline operator fun BigDecimal.div(other: BigDecimal): BigDecimal = this.divide(other, RoundingMode.HALF_EVEN)