Precedence dan asosiativitas menentukan urutan evaluasi sebuah ekspresi yang memuat banyak operator. Kotlin mengikuti urutan precedence yang konsisten dan dapat diprediksi, namun tidak sepenuhnya sama dengan Java atau C. Pada chapter ini kita akan menyajikan tabel precedence Kotlin lengkap, menjelaskan asosiativitas left dan right, lalu menunjukkan bagaimana urutan ini memengaruhi hasil perhitungan sehari-hari.
Tabel Precedence Kotlin #
Berikut urutan precedence dari tertinggi (dievaluasi pertama) ke terendah (dievaluasi terakhir):
| Tingkat | Kategori | Operator | Asosiativitas |
|---|---|---|---|
| 1 | Postfix | ++, --, ., ?., ? (index) | Left |
| 2 | Prefix | -, +, !, ++, --, label | Right |
| 3 | Type RHS | as, as?, : (type) | Left |
| 4 | Multiplicative | *, /, % | Left |
| 5 | Additive | +, - | Left |
| 6 | Range | .. | Left |
| 7 | Infix function | nama function infix | Left |
| 8 | Elvis | ?: | Right |
| 9 | Named checks | in, !in, is, !is | Left |
| 10 | Comparison | <, >, <=, >= | Left |
| 11 | Equality | ==, != | Left |
| 12 | Conjunction | && | Left |
| 13 | Disjunction | || | Left |
| 14 | Spread | * (di argumen variadic) | — |
| 15 | Assignment | =, +=, -=, *=, /=, %= | Right |
Operator dengan tingkat lebih tinggi dievaluasi lebih dulu. Bila dua operator berada pada tingkat yang sama, asosiativitas menentukan urutannya. Mayoritas operator binary bersifat left-associative, sehingga a - b - c dievaluasi sebagai (a - b) - c. Pengecualian: operator .. (range) dan assignment bersifat right-associative.
Contoh Ekspresi dan Evaluasi #
fun main() {
val a = 10
val b = 3
val c = 5
val hasil1 = a + b * c
println("a + b * c = $hasil1")
val hasil2 = (a + b) * c
println("(a + b) * c = $hasil2")
val hasil3 = a - b - c
println("a - b - c = $hasil3")
val hasil4 = a / b / c
println("a / b / c = $hasil4")
val rentang = 1..5
println("1..5 first=${rentang.first}, last=${rentang.last}")
val unary = -a + b
println("-a + b = $unary")
val elvis1: Int? = null
val hasil5 = elvis1 ?: a
println("null ?: a = $hasil5")
val banding = a > b && b > c || c == 0
println("(a > b && b > c) || c == 0 = $banding")
var x = 0
x += a + b * c
println("x += a + b * c -> $x")
val perbandingan = if (a > b) "besar" else "kecil"
println("a > b ? $perbandingan")
val stepRange = 1..10 step 2
println("1..10 step 2 -> ${stepRange.joinToString()}")
}Output yang Dihasilkan:
a + b * c = 25
(a + b) * c = 65
a - b - c = 2
a / b / c = 0
1..5 first=1, last=5
-a + b = -7
null ?: a = 10
(a > b && b > c) || c == 0 = false
x += a + b * c -> 25
a > b ? besar
1..10 step 2 -> 1, 3, 5, 7, 9Mari kita uraikan setiap ekspresi pada contoh di atas:
a + b * cmenghasilkan25. Operator*(multiplicative) lebih tinggi dari+(additive), sehingga evaluasi menjadia + (b * c)=10 + 15=25. Tanpa pemahaman precedence, kita mungkin salah mengira hasilnya(10 + 3) * 5 = 65.(a + b) * cmenghasilkan65karena tanda kurung memaksaa + bdievaluasi lebih dulu sebelum dikalikanc. Tanda kurung selalu menjadi precedence tertinggi bila ada.a - b - cmenghasilkan2. Operator-bersifat left-associative, sehingga evaluasi menjadi(a - b) - c=7 - 5=2. Bila bersifat right-associative, hasilnya akan menjadia - (b - c)=10 - (-2)=12, sangat berbeda.a / b / cmenghasilkan0. Evaluasi:(10 / 3) / 5=3 / 5=0(pembagian bulat). Inilah jebakan klasik: bila ingin pembagian float, gunakana.toDouble() / b / c.1..5membentuk rentang. Operator..sebenarnya adalah pemanggilanrangeTo()yang dipetakan ke tipeIntRange. Propertyfirstdanlastmembuktikan rentang inklusif di kedua sisi.-a + bmenghasilkan-7. Operator prefix-(unary) lebih tinggi dari+(binary), sehingga evaluasi menjadi(-a) + b=-10 + 3=-7. Tanpa precedence,-(a + b)akan menghasilkan-13.elvis1 ?: amenghasilkan10karenaelvis1adalahnull. Operator elvis?:mengembalikan operand kanan jika operand kiri null, sebaliknya mengembalikan operand kiri. Operator elvis bersifat right-associative, sehinggaa ?: b ?: cdievaluasi sebagaia ?: (b ?: c).a > b && b > c || c == 0menghasilkanfalse. Operator&&lebih tinggi dari||, sehingga evaluasi menjadi(a > b && b > c) || (c == 0)=(true && false) || false=false || false=false. Pemahaman precedence logika ini krusial saat menulis kondisi gabungan.x += a + b * cmenghasilkan25. Operator assignment+=bersifat right-associative dan berada di tingkat paling rendah. Sisi kanana + b * cdievaluasi penuh (25) sebelum ditambahkan kex. Tanpa precedence yang benar, ekspresi akan ambigu.1..10 step 2menghasilkan progresi1, 3, 5, 7, 9. Operatorstepadalah infix function berada di tingkat infix (lebih rendah dari..). Ekspresi dievaluasi sebagai(1..10) step 2, bukan1..(10 step 2).
Asosiativitas Right: Range dan Assignment #
Dua kategori operator yang bersifat right-associative:
- Range operator
..:a..b..cdievaluasi sebagaia..(b..c)secara konseptual, meskipun dalam praktik jarang ditulis berturut-turut. - Assignment
=,+=, dst.:a = b = cdievaluasi sebagaia = (b = c)di banyak bahasa; Kotlin membatasi chained assignment karena setiap penugasan harus berdiri sendiri sebagai statement.
Pada operator elvis ?:, sifat right-associative terasa alami: a ?: b ?: c ?: d berarti ambil a jika tidak null, jika null ambil b, jika b null ambil c, jika c null ambil d. Bentuk ini lebih natural dibanding jika elvis bersifat left-associative yang akan mengevaluasi (a ?: b) ?: (c ?: d) — meskipun hasil akhirnya sama untuk null checking, pola penulisan right lebih mudah dibaca.
Aturan Bonus: Infix Function Precedence #
Function infix berada di tingkat 7, di antara .. dan operator elvis. Akibatnya, 1..10 contains 5 dievaluasi sebagai (1..10) contains 5, bukan 1..(10 contains 5). Hal ini konsisten dengan ekspektasi bahwa rentang dibentuk dulu sebelum pemeriksaan keanggotaan.
Referensi Lanjutan #
- Operator overloading — konteks precedence pada konvensi operator Kotlin
Dengan menguasai tabel precedence dan asosiativitas, kita dapat menulis ekspresi yang panjang dengan keyakinan akan urutan evaluasi yang benar. Selanjutnya, kita akan mengulas perbedaan mendasar antara expression dan statement di Kotlin, yang akan memperjelas mengapa if dan when dapat mengembalikan nilai.