Operator Overloading trong Kotlin
1. Giới thiệu
Kotlin cho phép overload các operators (toán tử) cho các kiểu dữ liệu tự định nghĩa. Điều này cho phép sử dụng cú pháp tự nhiên như a + b, a[i], a in collection với các class của bạn.
2. Arithmetic Operators
Plus, Minus, Times, Div, Rem
| Expression | Function name |
|---|---|
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) |
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point) = Point(x + other.x, y + other.y)
operator fun minus(other: Point) = Point(x - other.x, y - other.y)
operator fun times(scale: Int) = Point(x * scale, y * scale)
}
fun main() {
val p1 = Point(10, 20)
val p2 = Point(5, 10)
println(p1 + p2) // Point(x=15, y=30)
println(p1 - p2) // Point(x=5, y=10)
println(p1 * 3) // Point(x=30, y=60)
}Unary Operators
| Expression | Function name |
|---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
++a, a++ | a.inc() |
--a, a-- | a.dec() |
data class Counter(val value: Int) {
operator fun unaryMinus() = Counter(-value)
operator fun inc() = Counter(value + 1)
operator fun dec() = Counter(value - 1)
}
fun main() {
var counter = Counter(5)
println(-counter) // Counter(value=-5)
println(++counter) // Counter(value=6)
println(--counter) // Counter(value=5)
}3. Compound Assignment Operators
| Expression | Function name |
|---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
class MutablePoint(var x: Int, var y: Int) {
operator fun plusAssign(other: MutablePoint) {
x += other.x
y += other.y
}
}
fun main() {
val point = MutablePoint(10, 20)
point += MutablePoint(5, 5)
println("(${point.x}, ${point.y})") // (15, 25)
}Lưu ý: Nếu định nghĩa cả
plusvàplusAssign, Kotlin sẽ ưu tiênplusAssigncho+=vớivar.
4. Comparison Operators
equals (==, !=)
data class Person(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
if (other !is Person) return false
return name == other.name && age == other.age
}
override fun hashCode() = name.hashCode() * 31 + age
}
fun main() {
val p1 = Person("Alice", 30)
val p2 = Person("Alice", 30)
val p3 = Person("Bob", 25)
println(p1 == p2) // true
println(p1 == p3) // false
println(p1 != p3) // true
}Note:
data classtự động generateequals()vàhashCode().
compareTo (<, >, <=, >=)
| Expression | Function call |
|---|---|
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 Version(val major: Int, val minor: Int, val patch: Int) : Comparable<Version> {
override fun compareTo(other: Version): Int {
return when {
major != other.major -> major - other.major
minor != other.minor -> minor - other.minor
else -> patch - other.patch
}
}
}
fun main() {
val v1 = Version(1, 0, 0)
val v2 = Version(2, 0, 0)
val v3 = Version(1, 5, 0)
println(v1 < v2) // true
println(v3 > v1) // true
println(v1 <= v1) // true
}5. Indexed Access Operators
get và set
| Expression | Function call |
|---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
class Matrix(private val rows: Int, private val cols: Int) {
private val data = Array(rows) { IntArray(cols) }
operator fun get(row: Int, col: Int): Int {
return data[row][col]
}
operator fun set(row: Int, col: Int, value: Int) {
data[row][col] = value
}
}
fun main() {
val matrix = Matrix(3, 3)
matrix[0, 0] = 1
matrix[1, 1] = 5
matrix[2, 2] = 9
println(matrix[0, 0]) // 1
println(matrix[1, 1]) // 5
}Ví dụ với Map-like class
class MutableProperties {
private val map = mutableMapOf<String, Any?>()
operator fun get(key: String): Any? = map[key]
operator fun set(key: String, value: Any?) {
map[key] = value
}
}
fun main() {
val props = MutableProperties()
props["name"] = "Alice"
props["age"] = 30
println(props["name"]) // Alice
println(props["age"]) // 30
}6. in Operator (contains)
| Expression | Function call |
|---|---|
a in b | b.contains(a) |
a !in b | !b.contains(a) |
data class Rectangle(val x: Int, val y: Int, val width: Int, val height: Int) {
operator fun contains(point: Point): Boolean {
return point.x in x until (x + width) &&
point.y in y until (y + height)
}
}
data class Point(val x: Int, val y: Int)
fun main() {
val rect = Rectangle(0, 0, 100, 100)
val p1 = Point(50, 50)
val p2 = Point(150, 50)
println(p1 in rect) // true
println(p2 in rect) // false
println(p2 !in rect) // true
}7. Range Operators
rangeTo (..)
| Expression | Function call |
|---|---|
a..b | a.rangeTo(b) |
a..<b | a.rangeUntil(b) |
data class Date(val year: Int, val month: Int, val day: Int) : Comparable<Date> {
override fun compareTo(other: Date): Int {
return compareValuesBy(this, other, Date::year, Date::month, Date::day)
}
operator fun rangeTo(other: Date) = DateRange(this, other)
}
class DateRange(
override val start: Date,
override val endInclusive: Date
) : ClosedRange<Date>, Iterable<Date> {
override fun iterator(): Iterator<Date> = DateIterator(start, endInclusive)
}
class DateIterator(start: Date, private val end: Date) : Iterator<Date> {
private var current = start
override fun hasNext() = current <= end
override fun next(): Date {
val result = current
current = current.nextDay()
return result
}
}
fun Date.nextDay(): Date {
// Simplified next day logic
return Date(year, month, day + 1)
}
fun main() {
val start = Date(2024, 1, 1)
val end = Date(2024, 1, 3)
for (date in start..end) {
println(date)
}
}8. Invoke Operator
Cho phép gọi object như function:
class Greeter(private val greeting: String) {
operator fun invoke(name: String) {
println("$greeting, $name!")
}
}
fun main() {
val greeter = Greeter("Hello")
greeter("Alice") // Hello, Alice!
greeter("Bob") // Hello, Bob!
}Ví dụ: Builder pattern
class HtmlBuilder {
private val content = StringBuilder()
operator fun invoke(block: HtmlBuilder.() -> Unit): String {
block()
return content.toString()
}
fun div(text: String) {
content.append("<div>$text</div>")
}
fun p(text: String) {
content.append("<p>$text</p>")
}
}
fun main() {
val html = HtmlBuilder()
val result = html {
div("Hello")
p("World")
}
println(result) // <div>Hello</div><p>World</p>
}9. Iterator và Destructuring
Iterator operator
class Countdown(private val start: Int) {
operator fun iterator(): Iterator<Int> {
return object : Iterator<Int> {
private var current = start
override fun hasNext() = current >= 0
override fun next() = current--
}
}
}
fun main() {
for (i in Countdown(5)) {
print("$i ") // 5 4 3 2 1 0
}
}Destructuring (componentN)
class Person(val name: String, val age: Int, val city: String) {
operator fun component1() = name
operator fun component2() = age
operator fun component3() = city
}
fun main() {
val person = Person("Alice", 30, "NYC")
val (name, age, city) = person
println("$name, $age, $city") // Alice, 30, NYC
// Bỏ qua một số components
val (n, _, c) = person
println("$n from $c") // Alice from NYC
}Note:
data classtự động generatecomponentN()functions.
10. Extension Operator Functions
Có thể định nghĩa operators như extension functions:
// Extension operator cho String
operator fun String.times(n: Int): String {
return this.repeat(n)
}
// Extension operator cho List
operator fun <T> List<T>.times(n: Int): List<T> {
return (1..n).flatMap { this }
}
fun main() {
println("Ha" * 3) // HaHaHa
println(listOf(1, 2) * 3) // [1, 2, 1, 2, 1, 2]
}📝 Tóm tắt
| Category | Operators | Functions |
|---|---|---|
| Arithmetic | +, -, *, /, % | plus, minus, times, div, rem |
| Unary | +a, -a, !a, ++, -- | unaryPlus, unaryMinus, not, inc, dec |
| Comparison | ==, != | equals |
| Comparison | <, >, <=, >= | compareTo |
| Index | a[i], a[i] = b | get, set |
| Contains | in, !in | contains |
| Range | .., ..< | rangeTo, rangeUntil |
| Invoke | a() | invoke |
Best practices:
- Chỉ overload operators khi có ý nghĩa logic rõ ràng
- Operators nên có behavior nhất quán với kỳ vọng của người dùng
- Sử dụng
data classđể tự động cóequals,hashCode,componentN
Last updated on