equals, hashCode, toString
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Kotlin של Coddy. שיעור 35 מתוך 57.
כל מחלקה יורשת את toString(), את equals() ואת hashCode() מ־Any. הגרסאות שעברו בירושה פועלות על סמך זהות: toString() מחזירה את שם המחלקה וקוד גיבוב, ו־equals() היא true רק עבור אותו אובייקט. דריסה של toString() מעניקה לאובייקטים טקסט קריא, שבו משתמשות println ותבניות מחרוזת:
class Money(val cents: Int) {
override fun toString() = "$%d.%02d".format(cents / 100, cents % 100)
}בתוך main:
val price = Money(1250)
println(price)
println("total: ${Money(99)}")פלט:
$12.50
total: $0.99== קורא ל-equals(), ואילו === בודק אם שתי הפניות מצביעות על אותו אובייקט. כדי להשוות לפי ערך, יש לדרוס את equals. הפרמטר שלו הוא Any?, ולכן הוא בודק תחילה את הטיפוס ואז משווה את המאפיינים הרלוונטיים:
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()
}בתוך main:
val a = Isbn("978-0-441-17271-9")
val b = Isbn("9780441172719")
println(a == b)
println(a === b)פלט:
true
falseלאובייקטים שווים חייבים להיות קודי hash שווים. HashSet ו-HashMap מוצאים רשומות תחילה באמצעות hashCode() ורק לאחר מכן קוראים ל-equals(), לכן דריסת equals בלי hashCode גורמת לאובייקטים שווים להיראות שונים עבורם. יש לדרוס תמיד את שניהם ולחשב אותם מאותן תכונות:
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"
}בתוך 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)פלט:
2
trueמחלקת נתונים יוצרת את שלושתם מתוך מאפייני הבנאי שלה. כתבו אותם בעצמכם כאשר השוויון צריך להתבסס על פחות מאפיינים או על צורה מנורמלת, כגון כתובת אימייל שאינה מבחינה בין אותיות גדולות לקטנות:
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>"
}בתוך main:
val users = listOf(User("Ada@Example.com", "Ada"), User("ada@example.com ", "A. Lovelace"))
println(users.distinct())פלט:
[Ada <ada@example.com>]אתגר
קלשני מוצרים זהים כאשר ה-sku שלהם זהה, ללא קשר למה שכתוב בשדות האחרים. דרוס את equals() ואת hashCode() ב-Product על סמך sku בלבד, ואת toString() כך שמוצר יודפס בתור A1 tea 450. לאחר מכן השלימו את mergeCatalog(products), שמשאירה את ההופעה הראשונה של כל מוצר בסדר המקורי.
הקוד שסופק קורא מוצרים בשורות בפורמט sku,name,price, ואת השורה האחרונה עם sku לחיפוש. הוא מדפיס כל מוצר שנשמר, את removed: 2 ואת A1 in catalog: true, ומחפש את ה-sku בתוך HashSet.
הקוד שלכם ייכתב בקבצים Product.kt ו-Functions.kt. הקובץ Main.kt מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.
נסו בעצמכם
fun main() {
// קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
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()}")
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1מחלקות ואובייקטים
עבודה עם קבציםהגדרת מחלקותבנאים ראשייםבלוקי init ואימות נתוניםבנאים משנייםסיכום – כרטיס ספרייה7מחלקות מיוחדות
מחלקות נתוניםמחלקות Enumהיררכיות אטומותequals, hashCode, toStringחזרה – מעקב אחר הזמנותתרגלו בעצמכם: Kotlin אונליין