0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlin:`operator` 関数(演算子オーバーロード)徹底解説

Posted at

はじめに

Kotlin では、
+, -, *, [], in などの演算子
自作クラスに自然に使えるようにできます。

これが「operator overloading(演算子のオーバーロード)」です。


1. 基本構文

Kotlin では、
特定の関数名に operator 修飾子をつけることで、
演算子として呼び出せるようになります。

data class Vec(val x: Int, val y: Int) {
    operator fun plus(other: Vec) = Vec(x + other.x, y + other.y)
}

これで次のように書けます。

val a = Vec(1, 2)
val b = Vec(3, 4)
val c = a + b  // ← operator fun plus が呼ばれる
println(c)     // Vec(x=4, y=6)

内部的には a.plus(b) と同じ意味です。


2. 対応できる operator 一覧(代表的なもの)

演算子 対応関数名
+ plus a + b
- minus a - b
* times a * b
/ div a / b
% rem a % b
++ / -- inc / dec a++, --a
[] get / set a[i], a[i] = v
in contains x in list
== / != equals a == b
> / < / >= / <= compareTo a > b
() invoke a()
.. rangeTo 1..5
+= / -= plusAssign / minusAssign list += x

3. 加算・減算などの例

data class Vec(val x: Int, val y: Int) {
    operator fun plus(other: Vec) = Vec(x + other.x, y + other.y)
    operator fun minus(other: Vec) = Vec(x - other.x, y - other.y)
}

fun main() {
    val a = Vec(5, 10)
    val b = Vec(2, 3)
    println(a + b) // Vec(x=7, y=13)
    println(a - b) // Vec(x=3, y=7)
}

4. インクリメント・デクリメント

data class Counter(var value: Int) {
    operator fun inc() = Counter(value + 1)
    operator fun dec() = Counter(value - 1)
}

fun main() {
    var c = Counter(10)
    println(++c) // Counter(value=11)
    println(c--) // Counter(value=11)(postfix)
    println(c)   // Counter(value=10)
}

5. 比較演算子 compareTo

<, >, <=, >= を有効にするには compareTo を定義します。

data class Version(val major: Int, val minor: Int): Comparable<Version> {
    override operator fun compareTo(other: Version): Int {
        return if (major != other.major)
            major - other.major
        else
            minor - other.minor
    }
}

fun main() {
    val v1 = Version(1, 2)
    val v2 = Version(1, 5)
    println(v1 < v2) // true
}

Kotlinでは内部的に v1.compareTo(v2) < 0 と評価されます。


6. get / set で添字アクセス

class Matrix {
    private val data = Array(3) { IntArray(3) }

    operator fun get(i: Int, j: Int) = data[i][j]
    operator fun set(i: Int, j: Int, value: Int) {
        data[i][j] = value
    }
}

fun main() {
    val m = Matrix()
    m[1, 2] = 42
    println(m[1, 2]) // 42
}

a[i, j]a.get(i, j)
a[i, j] = va.set(i, j, v)


7. incontains

data class User(val name: String)

class UserGroup(private val members: List<User>) {
    operator fun contains(user: User): Boolean {
        return members.any { it.name == user.name }
    }
}

fun main() {
    val group = UserGroup(listOf(User("Anna"), User("Bob")))
    println(User("Anna") in group) // true
    println(User("Eve") in group)  // false
}

内部的には group.contains(user) を呼び出しています。


8. invoke 演算子で関数のように呼び出す

class Greeter(val message: String) {
    operator fun invoke(name: String) {
        println("$message, $name!")
    }
}

fun main() {
    val hello = Greeter("Hello")
    hello("Anna") // Hello, Anna!
}

hello.invoke("Anna") の糖衣構文。
DSL風・関数オブジェクトの設計に最適。


9. plusAssign / minusAssign+= に対応

class Bag<T> {
    private val items = mutableListOf<T>()

    operator fun plusAssign(item: T) {
        items.add(item)
    }

    override fun toString() = items.toString()
}

fun main() {
    val bag = Bag<String>()
    bag += "Apple"
    bag += "Banana"
    println(bag) // [Apple, Banana]
}

+=plusAssign()
-=minusAssign()


10. rangeToiterator

範囲表現(..)や for ループもカスタマイズ可能!

data class Vec(val x: Int, val y: Int)

data class VecRange(val start: Vec, val endInclusive: Vec)

operator fun Vec.rangeTo(other: Vec): VecRange = VecRange(this, other)

fun main() {
    val a = Vec(1, 2)
    val b = Vec(5, 6)
    val range = a..b
    println(range) // VecRange(start=Vec(x=1, y=2), endInclusive=Vec(x=5, y=6))
}


11. operator × DSL の一例(応用)

class Router {
    private val routes = mutableMapOf<String, String>()

    operator fun String.invoke(handler: String) {
        routes[this] = handler
    }

    fun show() = println(routes)
}

fun main() {
    val router = Router()
    with(router) {
        "/home"("HomeHandler")
        "/user"("UserHandler")
    }
    router.show()
}

出力:

{/home=HomeHandler, /user=UserHandler}

これは簡易 DSL(ドメイン固有言語)としても使える例です。


まとめ

  • operator演算子の挙動を関数で定義できる強力な仕組み
  • data class だけでなく、独自クラスでも自然な構文が書ける
  • invoke, get, contains などを使うと DSL・宣言的コード設計 にも応用可能
  • Kotlin のシンタックスシュガーを 安全に制御できる のが最大の魅力

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?