演算子オーバーロード
| 対応: | Kotlin 1.0(2016) |
|---|
Kotlinでは『operator』キーワードを使って、演算子(+, -, *, [], () など)を独自クラスで再定義できます。演算子オーバーロードにより直感的な記法でオブジェクトを操作できます。
構文
data class Point(val x: Int, val y: Int) {
// + 演算子のオーバーロード
operator fun plus(other: Point): Point = Point(x + other.x, y + other.y)
// - 演算子のオーバーロード
operator fun minus(other: Point): Point = Point(x - other.x, y - other.y)
// 単項マイナス
operator fun unaryMinus(): Point = Point(-x, -y)
}
演算子一覧
| 演算子 | 関数名 | 概要 |
|---|---|---|
| +a | unaryPlus() | 単項プラスです。 |
| -a | unaryMinus() | 単項マイナスです。 |
| a + b | plus(b) | 加算です。 |
| a - b | minus(b) | 減算です。 |
| a * b | times(b) | 乗算です。 |
| a / b | div(b) | 除算です。 |
| a % b | rem(b) | 剰余です。 |
| a[i] | get(i) | インデックスアクセスです。 |
| a[i] = v | set(i, v) | インデックス代入です。 |
| a() | invoke() | 関数呼び出しです。 |
| a in b | contains(a) | 包含チェックです。 |
| a..b | rangeTo(b) | 範囲生成です。 |
サンプルコード
sample_operator_overloading.kt
data class Vector(val x: Double, val y: Double) {
operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
operator fun minus(other: Vector) = Vector(x - other.x, y - other.y)
operator fun times(scale: Double) = Vector(x * scale, y * scale)
operator fun unaryMinus() = Vector(-x, -y)
// invoke() でオブジェクトを関数のように呼び出せる(大きさを返す)
operator fun invoke(): Double = Math.sqrt(x * x + y * y)
override fun toString() = "($x, $y)"
}
// 発展的な使い方
class Matrix2x2(val a: Double, val b: Double, val c: Double, val d: Double) {
// [] でアクセスできる(row, col は 0 始まり)
operator fun get(row: Int, col: Int): Double = when {
row == 0 && col == 0 -> a
row == 0 && col == 1 -> b
row == 1 && col == 0 -> c
else -> d
}
}
fun main() {
val v1 = Vector(3.0, 4.0)
val v2 = Vector(1.0, 2.0)
println(v1 + v2) // (4.0, 6.0)
println(v1 - v2) // (2.0, 2.0)
println(v1 * 2.0) // (6.0, 8.0)
println(-v1) // (-3.0, -4.0)
println(v1()) // 5.0(大きさ: √(9+16))
val mat = Matrix2x2(1.0, 2.0, 3.0, 4.0)
println(mat[0, 0]) // 1.0
println(mat[1, 1]) // 4.0
}
コンパイルして実行すると次のようになります。
kotlinc sample_operator_overloading.kt -include-runtime -d sample_operator_overloading.jar java -jar sample_operator_overloading.jar (4.0, 6.0) (2.0, 2.0) (6.0, 8.0) (-3.0, -4.0) 5.0 1.0 4.0
よくあるミス
// operator キーワードを付け忘れて演算子として呼べない
data class Score(val value: Int) {
fun plus(other: Score): Score = Score(value + other.value) // operator がない
}
fun main() {
val s1 = Score(120)
val s2 = Score(90)
// val s3 = s1 + s2 // コンパイルエラー: unresolved reference: plus
val s3 = s1.plus(s2) // 通常の関数呼び出しは可能
println(s3) // Score(value=210)
// times(Double) を定義して Int との乗算で使えないと気づかない
// val v = Vector(1.0, 2.0) * 3 // コンパイルエラー: operator fun times(scale: Double) なので Int は不可
}
よくあるミス2として、『Comparable』を実装しないと比較演算子(『>』『<』)が使えない。正しい書き方は次のとおり。
sample_operator_overloading_ok.kt
// operator を付けた正しい定義
data class DeathNotes(val count: Int) {
operator fun plus(other: DeathNotes): DeathNotes = DeathNotes(count + other.count)
}
// Comparable を実装すれば比較演算子が使える
data class Intelligence(val iq: Int) : Comparable<Intelligence> {
override fun compareTo(other: Intelligence): Int = iq - other.iq
}
fun main() {
val d1 = DeathNotes(1)
val d2 = DeathNotes(2)
println(d1 + d2) // DeathNotes(count=3)
val light = Intelligence(167)
val l_lawliet = Intelligence(212)
println(light < l_lawliet) // true
}
コンパイルして実行すると次のようになります。
kotlinc sample_operator_overloading_ok.kt -include-runtime -d sample_operator_overloading_ok.jar java -jar sample_operator_overloading_ok.jar DeathNotes(count=3) true
概要
演算子オーバーロードは数値計算・座標・行列・複素数など、数学的な概念を扱うクラスで特に効果を発揮します。ただし、直感に反する使い方は混乱を招くため、意味が自明な場合にのみ使うことを推奨します。
『invoke()』をオーバーロードするとオブジェクトを関数のように呼び出せます。これはDSLやビルダーパターンに活用できます。
データクラスの機能はdata classを、クラスの継承はopen / overrideを参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。