equals, hashCode, toString
Thuộc phần Lập trình hướng đối tượng trong hành trình Kotlin của Coddy. Bài học 35 trên 57.
Mọi lớp đều kế thừa toString(), equals() và hashCode() từ Any. Các phiên bản được kế thừa hoạt động dựa trên danh tính: toString() trả về tên lớp và mã băm, còn equals() chỉ là true khi đó là cùng một đối tượng. Ghi đè toString() giúp đối tượng có văn bản dễ đọc, được dùng bởi println và mẫu chuỗi:
class Money(val cents: Int) {
override fun toString() = "$%d.%02d".format(cents / 100, cents % 100)
}Bên trong main:
val price = Money(1250)
println(price)
println("total: ${Money(99)}")Đầu ra:
$12.50
total: $0.99== gọi equals(), còn === kiểm tra xem hai tham chiếu có trỏ đến cùng một đối tượng hay không. Để so sánh theo giá trị, hãy ghi đè equals. Tham số của nó là Any?, nên hàm kiểm tra kiểu trước rồi so sánh các thuộc tính liên quan:
class Isbn(code: String) {
val digits = code.filter { it.isDigit() }
override fun equals(other: Any?) = other is Isbn && other.digits == digits
override fun hashCode() = digits.hashCode()
}Bên trong main:
val a = Isbn("978-0-441-17271-9")
val b = Isbn("9780441172719")
println(a == b)
println(a === b)Đầu ra:
true
falseCác đối tượng bằng nhau phải có mã băm bằng nhau. HashSet và HashMap tìm các mục bằng hashCode() trước, rồi mới gọi equals(), vì vậy nếu ghi đè equals mà không ghi đè hashCode, các đối tượng bằng nhau sẽ có vẻ khác nhau trong chúng. Luôn ghi đè cả hai, tính từ cùng các thuộc tính:
class Isbn(code: String) {
val digits = code.filter { it.isDigit() }
override fun equals(other: Any?) = other is Isbn && other.digits == digits
override fun hashCode() = digits.hashCode()
override fun toString() = "ISBN $digits"
}Bên trong main:
val shelf = hashSetOf(Isbn("978-0-441-17271-9"), Isbn("9780441172719"), Isbn("0-14-143951-3"))
println(shelf.size)
println(Isbn("9780441172719") in shelf)Đầu ra:
2
trueMột lớp dữ liệu tự tạo cả ba phương thức từ các thuộc tính của hàm khởi tạo. Hãy tự viết các phương thức này khi phép so sánh bằng nhau nên dùng ít thuộc tính hơn hoặc một dạng đã chuẩn hóa, chẳng hạn như địa chỉ email không phân biệt chữ hoa chữ thường:
class User(val email: String, val name: String) {
private val key = email.trim().lowercase()
override fun equals(other: Any?) = other is User && other.key == key
override fun hashCode() = key.hashCode()
override fun toString() = "$name <$key>"
}Bên trong main:
val users = listOf(User("Ada@Example.com", "Ada"), User("ada@example.com ", "A. Lovelace"))
println(users.distinct())Đầu ra:
[Ada <ada@example.com>]Thử thách
DễHai sản phẩm giống nhau khi sku của chúng khớp nhau, bất kể các trường khác có giá trị gì. Chỉ ghi đè equals() và hashCode() trong Product dựa trên sku, đồng thời ghi đè toString() để sản phẩm được in ra dưới dạng A1 tea 450. Sau đó hoàn thành mergeCatalog(products), hàm này giữ lại lần xuất hiện đầu tiên của mỗi sản phẩm theo thứ tự ban đầu.
Code có sẵn đọc các sản phẩm từ các dòng theo định dạng sku,name,price và dòng cuối cùng chứa một sku cần tra cứu. Code in ra mọi sản phẩm được giữ lại, removed: 2 và A1 in catalog: true, đồng thời tra cứu sku trong một HashSet.
Code của bạn nằm trong Product.kt và Functions.kt. Main.kt chứa code đầu vào/đầu ra có sẵn và bạn không thể chỉnh sửa tệp này.
Tự làm thử
fun main() {
// Đoạn code đầu vào/đầu ra đã cung cấp: giữ nguyên
val input = generateSequence(::readLine).toList()
val products = input.dropLast(1).map { line ->
val (sku, name, price) = line.split(",")
Product(sku, name, price.toInt())
}
val kept = mergeCatalog(products)
for (p in kept) println(p)
println("removed: ${products.size - kept.size}")
val probe = Product(input.last(), "?", 0)
println("${input.last()} in catalog: ${probe in kept.toHashSet()}")
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpHàm khởi tạo chínhKhối init và kiểm tra tính hợp lệHàm khởi tạo phụÔn tập: thẻ thư viện4Kế thừa
Lớp mở và lớp conGhi đè thành viênsuper và hàm khởi tạoLớp trừu tượngÔn tập: Danh bạ nhân viên7Các lớp đặc biệt
Lớp dữ liệuLớp enumHệ phân cấp sealedequals, hashCode, toStringÔn tập: Theo dõi đơn hàng2Thuộc tính và tính đóng gói
Trình truy cập tùy chỉnhBộ sửa đổi phạm vi truy cậpSetter riêng tưlazy và lateinitÔn tập: tài khoản ngân hàng5Giao diện
Khai báo giao diệnPhương thức mặc địnhNhiều giao diệnGiao diện như kiểu dữ liệuÔn tập: Phương thức thanh toánTự luyện tập: Playground Kotlin