Kotlinにおける「Operator」は、次の2つの視点で理解すると整理しやすくなります。
- 言語に組み込まれている演算子(構文)
- operatorキーワードによる演算子オーバーロード(構文糖衣)
Kotlinの演算子は単なる記号ではなく、すべて対応する関数呼び出しに変換される構文です。
ここがJavaとの大きな違いです。
目次
Kotlinの基本演算子
算術演算子
| 演算子 | 展開される関数 |
|---|---|
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) |
val a = 10
val b = 3
println(a + b) // 13
※ これは実質 a.plus(b) の呼び出しです。
代入系演算子
| 演算子 | 展開 |
|---|---|
a += b | a = a.plus(b) または a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
Kotlinでは、plusAssign が存在すればそれが優先され、存在しなければ a = a + b に変換されます。
比較演算子
==(構造的等価)
a == b は単純な equals() 呼び出しではありません。
実際には概ね次のように展開されます。
a?.equals(b) ?: (b === null)
つまり、
- null安全
- equalsを呼ぶ
- 両方nullならtrue
という仕様です。
===(参照同一性)
a === b
これは参照が同一かどうかを判定します。
オーバーロードは不可能です。
比較
| 演算子 | 展開 |
|---|---|
a < b | a.compareTo(b) < 0 |
a > b | a.compareTo(b) > 0 |
a <= b | a.compareTo(b) <= 0 |
a >= b | a.compareTo(b) >= 0 |
data class Score(val value: Int) : Comparable<Score> {
override operator fun compareTo(other: Score): Int {
return value - other.value
}
}
範囲演算子
| 演算子 | 展開 |
|---|---|
a..b | a.rangeTo(b) |
for (i in 1..5) { }
in / !in
x in y
は
y.contains(x)
に展開されます。
添字演算子 []
a[i]
は
a.get(i)
に展開されます。
a[i] = value
は
a.set(i, value)
に展開されます。
正しい例
class MyCollection {
private val list = mutableListOf<Int>()
operator fun get(index: Int): Int = list[index]
operator fun set(index: Int, value: Int) {
require(index >= 0)
while (list.size <= index) list.add(0)
list[index] = value
}
}
fun main() {
val c = MyCollection()
c[0] = 10
println(c[0]) // 10
}
invoke演算子
a()
は
a.invoke()
に展開されます。
class Printer {
operator fun invoke(msg: String) {
println(msg)
}
}
val p = Printer()
p("Hello")
iterator(forループ)
for (x in collection)
は内部的に
collection.iterator()
を呼びます。
そのIteratorは
- hasNext()
- next()
を持つ必要があります。
インクリメント / デクリメント
Kotlinの ++ と -- は特殊です。
a++ の展開(概念的)
- 一時変数に a を保存
a = a.inc()- 式の値は元の a
++a の展開
a = a.inc()- 式の値も更新後
重要なのは
inc()の戻り値が変数へ再代入される- 言語仕様として「必ず不変」とは定義していない
- ただし実務では不変オブジェクト設計が一般的
data class Counter(val value: Int) {
operator fun inc(): Counter {
return Counter(value + 1)
}
}
単項演算子
| 演算子 | 関数 |
|---|---|
+a | unaryPlus |
-a | unaryMinus |
!a | not |
分解宣言(componentN)
val (x, y) = point
は
point.component1()
point.component2()
に展開されます。
data classは自動生成します。
委譲プロパティ演算子
val name by delegate
は
- getValue
- setValue
に展開されます。
高度なDSL設計で使われます。
Kotlin Operatorの本質
Kotlinの演算子はすべて
記号ではなく、対応する関数への変換ルール
です。
つまり、Kotlinの演算子設計は
- 可読性のための構文糖衣
- DSL構築を強く意識した設計
- Javaと違い、限定的にオーバーロード可能
という思想に基づいています。
実務でのベストプラクティス
数学的概念には有効
- ベクトル
- マネー型
- 複素数
DSL設計に非常に強い
Gradle Kotlin DSL
Jetpack Compose
乱用は可読性を落とす
演算子は「直感的に意味が明確な場合のみ」使うべきです。
まとめ
KotlinのOperatorは次の5層構造で理解できます。
- 基本演算子(算術・比較)
- null安全演算子(?:, ?., !!)
- 構文糖衣としての展開ルール
- operatorキーワードによるオーバーロード
- DSL応用(invoke, get, componentN など)
以上、KotlinのOperatorについてでした。
最後までお読みいただき、ありがとうございました。










