Operator overloading memungkinkan kita mendefinisikan perilaku +, -, *, [], in, dan operator lain pada class buatan sendiri. Kotlin menerapkan aturan ketat: hanya operator dengan nama function tertentu (plus, minus, times, dst.) yang dapat dipanggil lewat simbol. Setiap function harus ditandai dengan modifier operator. Pada chapter ini kita akan membangun class Vec yang mendukung +, -, *, [], dan in, lalu mengulas daftar operator yang dapat di-overload.

Daftar Operator yang Dapat Di-overload #

Berikut ringkasan operator utama beserta function konvensinya:

SimbolFunctionContoh deklarasiPemakaian
+plus()operator fun plus(o: T): Ta + b
-minus()operator fun minus(o: T): Ta - b
*times()operator fun times(n: Int): Ta * 2
/div()operator fun div(n: Int): Ta / 2
%rem()operator fun rem(n: Int): Ta % 2
<, >, <=, >=compareTo()operator fun compareTo(o: T): Inta < b
[] (baca)get()operator fun get(i: Int): Ea[0]
[] (tulis)set()operator fun set(i: Int, v: E)a[0] = v
incontains()operator fun contains(e: E): Booleanx in a
..rangeTo()operator fun rangeTo(o: T): Ra..b
()invoke()operator fun invoke(arg: A): Ra(arg)
+=plusAssign()operator fun plusAssign(e: E)a += b

Modifier operator wajib ada. Tanpa modifier, function tetap dapat dipanggil secara normal sebagai a.plus(b), tetapi tidak dapat dipanggil lewat simbol a + b.

Mendefinisikan Operator pada Vec #

Berikut class Vec dua dimensi yang mendukung penjumlahan, pengurangan, perkalian skalar, indeks berbasis sumbu, dan pemeriksaan keanggotaan titik:

kotlin
data class Vec(val x: Int, val y: Int) {
    operator fun plus(other: Vec): Vec = Vec(x + other.x, y + other.y)
    operator fun minus(other: Vec): Vec = Vec(x - other.x, y - other.y)
    operator fun times(scalar: Int): Vec = Vec(x * scalar, y * scalar)
    operator fun get(axis: Char): Int = when (axis) {
        'x' -> x
        'y' -> y
        else -> 0
    }
    operator fun contains(point: Vec): Boolean = point.x in 0..x && point.y in 0..y
    override fun toString(): String = "Vec($x, $y)"
}

fun main() {
    val a = Vec(3, 4)
    val b = Vec(1, 2)

    println("a = $a")
    println("b = $b")
    println("a + b = ${a + b}")
    println("a - b = ${a - b}")
    println("a * 2 = ${a * 2}")
    println("a['x'] = ${a['x']}")
    println("a['y'] = ${a['y']}")

    val pojok = Vec(2, 2)
    println("Vec(2,2) in a: ${pojok in a}")
    println("Vec(5,5) in a: ${Vec(5, 5) in a}")

    val daftar = listOf(a, b, Vec(0, 0))
    println("Daftar: $daftar")
    val total = daftar.fold(Vec(0, 0)) { acc, v -> acc + v }
    println("Total fold: $total")
}

Output yang Dihasilkan:

text
a = Vec(3, 4)
b = Vec(1, 2)
a + b = Vec(4, 6)
a - b = Vec(2, 2)
a * 2 = Vec(6, 8)
a['x'] = 3
a['y'] = 4
Vec(2,2) in a: true
Vec(5,5) in a: false
Daftar: [Vec(3, 4), Vec(1, 2), Vec(0, 0)]
Total fold: Vec(4, 6)

Mari kita bedah setiap bagian pada contoh di atas:

  1. operator fun plus(other: Vec): Vec mendefinisikan perilaku a + b. Function mengembalikan Vec baru dengan komponen yang dijumlahkan. Modifier operator wajib; tanpa modifier, simbol + tidak akan dikenali.
  2. operator fun times(scalar: Int): Vec mendefinisikan perkalian dengan skalar. Tipe operand kedua berbeda (Int), namun Kotlin mengizinkan tipe operand berbeda pada operator yang di-overload. Tidak ada operator otomatis 2 * a (operand pertama Int); bila diperlukan, definisikan extension function operator fun Int.times(v: Vec): Vec.
  3. operator fun get(axis: Char): Int mendefinisikan akses indeks a['x']. Parameter tidak harus Int — kita memakai Char sebagai sumbu. Fleksibilitas ini memungkinkan API yang ekspresif seperti matrix['x', 'y'].
  4. operator fun contains(point: Vec): Boolean mendefinisikan operator in. Pemeriksaan pojok in a sekarang berarti apakah pojok berada dalam kotak dari origin ke a. Tidak ada hubungan dengan pemeriksaan kesetaraan nilai; semantik in ditentukan sepenuhnya oleh implementasi function.
  5. daftar.fold(Vec(0, 0)) { acc, v -> acc + v } menjumlahkan semua Vec dalam list menggunakan operator + yang sudah di-overload. Lambda di dalam fold menggunakan acc + v, yang dipetakan ke pemanggilan acc.plus(v). Tanpa operator overloading, kode ini akan menjadi acc.plus(v) yang lebih bertele-tele.

Aturan Penting Operator Overloading #

Kotlin menerapkan beberapa aturan untuk menjaga keterbacaan dan keamanan:

AturanPenjelasan
Modifier operator wajibMencegah function biasa tak sengaja dipanggil lewat simbol
Nama function harus tepatplus() untuk +, bukan add() atau nama lain
Tidak boleh membuat operator baruKita tidak dapat menambahkan %% atau ^^; hanya simbol yang sudah dikenal
Precedence tetap mengikuti standarOperator * selalu lebih tinggi dari + walau di-overload
Asosiativitas tetapOperator binary tetap left-associative kecuali .. dan assignment

Operator get, set, dan invoke #

Operator get dan set memungkinkan class kita berperilaku seperti array atau map. Operator invoke memungkinkan instance class dipanggil seperti function. Kombinasi ketiga operator ini sering dipakai untuk membangun DSL yang ekspresif:

kotlin Contoh: operator invoke pada class config
class Validator {
    operator fun invoke(input: String): Boolean =
        input.length in 1..100
}

val validator = Validator()
val ok = validator("data valid")  // true

Operator invoke juga bekerja dengan multi-parameter. Class yang mengimplementasikan operator fun invoke(a: A, b: B): R dapat dipanggil sebagai instance(a, b), seolah-olah instance adalah function.

Referensi Lanjutan #

Dengan menguasai operator overloading, kita dapat membangun API yang terbaca seperti aritmetika atau akses map. Selanjutnya, kita perlu memahami urutan precedence dan asosiativitas agar ekspresi yang menggunakan banyak operator dapat dievaluasi sesuai harapan.