Bỏ qua để đến nội dung

➕ Operator Overloading

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.

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)
}
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)
}
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ả plusplusAssign, Kotlin sẽ ưu tiên plusAssign cho += với var.

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 class tự động generate equals()hashCode().

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
}
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
}
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
}
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
}
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)
}
}

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!
}
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>
}
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
}
}
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 class tự động generate componentN() 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]
}

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