Skip to Content
Kotlin📘 Ngôn ngữ Kotlin⚖️ So sánh bằng

Equality (So sánh bằng) trong Kotlin

🎯 Mục tiêu: Hiểu rõ hai loại so sánh bằng trong Kotlin: Structural equality (==) và Referential equality (===), cùng các trường hợp đặc biệt với số thực và mảng.


💡 Tổng quan

Kotlin cung cấp hai loại so sánh bằng:

  • Structural equality (==): So sánh nội dung/giá trị (dùng hàm equals()).
  • Referential equality (===): So sánh địa chỉ vùng nhớ (hai tham chiếu cùng trỏ về một object).

📝 Structural Equality (==)

Structural equality kiểm tra xem hai đối tượng có “tương đương” về mặt nội dung hay không.

Toán tử == được dịch sang:

a == b // Tương đương với: a?.equals(b) ?: (b === null)

Điều này có nghĩa là == an toàn với null. Nếu anull, nó sẽ kiểm tra xem b có phải là null không (dùng ===).

fun main() { val a = "hello" val b = "hello" val c = null val d = null println(a == b) // true (nội dung giống nhau) println(a == c) // false println(c == d) // true (đều là null) }

Với Custom Class

Mặc định, các class trong Kotlin kế thừa equals() từ Any, so sánh theo tham chiếu (giống ===).

  • Data classes: Tự động override equals() để so sánh các properties trong primary constructor.
  • Regular classes: Cần tự override equals() nếu muốn so sánh theo nội dung.
class Point(val x: Int, val y: Int) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Point) return false // So sánh nội dung return this.x == other.x && this.y == other.y } // Luôn override hashCode khi override equals! override fun hashCode(): Int = 31 * x + y }

🔗 Referential Equality (===)

Referential equality kiểm tra xem hai biến có trỏ đến cùng một đối tượng trong bộ nhớ hay không.

fun main() { val a = Integer(100) val b = Integer(100) val c = a println(a == b) // true (giá trị giống nhau) println(a === b) // false (hai object khác nhau) println(a === c) // true (cùng trỏ đến object a) }
ℹ️

Với các kiểu nguyên thủy (như Int) tại runtime, === tương đương với ==.


🔢 So sánh số thực (Floating-point)

Khi các toán hạng là Float hoặc Double (tĩnh), phép so sánh tuân theo chuẩn IEEE 754.

Tuy nhiên, khi so sánh trong ngữ cảnh generic hoặc Any (được boxing), logic sẽ thay đổi để dảm bảo tính nhất quán của equals()Comparable:

  1. NaN bằng chính nó.
  2. NaN lớn hơn POSITIVE_INFINITY.
  3. -0.0 không bằng 0.0.
val zero = 0.0 val negZero = -0.0 println(zero == negZero) // true (IEEE 754) val boxedZero: Any = 0.0 val boxedNegZero: Any = -0.0 println(boxedZero == boxedNegZero) // false (Structural equality cho object)

📦 So sánh Mảng (Array Equality)

Mảng trong Kotlin không override equals() để so sánh phần tử (giống Java). arr1 == arr2 chỉ so sánh tham chiếu.

Để so sánh nội dung mảng, dùng contentEquals() hoặc contentDeepEquals():

val arr1 = arrayOf(1, 2, 3) val arr2 = arrayOf(1, 2, 3) println(arr1 == arr2) // false (khác tham chiếu) println(arr1.contentEquals(arr2)) // true (cùng nội dung)

🛠️ Thực hành

Bài tập 1: Data Class vs Regular Class

data class DataUser(val name: String) class RegularUser(val name: String) fun main() { val d1 = DataUser("Alice") val d2 = DataUser("Alice") val r1 = RegularUser("Bob") val r2 = RegularUser("Bob") // TODO: So sánh d1 với d2, r1 với r2 bằng `==` và giải thích kết quả }

Lời giải:

println(d1 == d2) // true (Data class tự override equals) println(r1 == r2) // false (Regular class dùng equals mặc định là ===)

✅ Checklist

  • Hiểu sự khác biệt giữa =====
  • Biết cách override equals() cho class thường
  • Hiểu hành vi so sánh NaN-0.0 khi boxing
  • Dùng contentEquals để so sánh mảng
Last updated on