האצלה באמצעות by
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Kotlin של Coddy. שיעור 46 מתוך 57.
האצלה פירושה שאובייקט מעביר את העבודה לאובייקט אחר במקום לרשת אותה. Kotlin תומכת בכך ישירות: class Polite(private val inner: Greeter) : Greeter by inner מממשת את Greeter על ידי העברת כל קריאה אל inner, והמחלקה דורסה רק את מה שהיא משנה:
interface Greeter {
fun hello(name: String): String
fun bye(name: String): String
}
class English : Greeter {
override fun hello(name: String) = "Hello, $name"
override fun bye(name: String) = "Bye, $name"
}
class Polite(private val inner: Greeter) : Greeter by inner {
override fun hello(name: String) = inner.hello(name) + ", pleased to meet you"
}בתוך main:
val g = Polite(English())
println(g.hello("Ada"))
println(g.bye("Ada"))פלט:
Hello, Ada, pleased to meet you
Bye, Adaהאצלה היא חלופה לירושה. היא עובדת עם ממשקים שהמימושים שלהם סופיים, והאובייקט המואצל יכול להיות כל אובייקט שנבחר בזמן ריצת התוכנית. הקבוצה הזו סופרת כל add ומעבירה את כל השאר ל־MutableSet רגיל:
class CountingSet<T>(private val inner: MutableSet<T> = mutableSetOf()) : MutableSet<T> by inner {
var attempts = 0
private set
override fun add(element: T): Boolean {
attempts++
return inner.add(element)
}
}בתוך main:
val tags = CountingSet<String>()
tags.add("kotlin")
tags.add("jvm")
tags.add("kotlin")
println("${tags.size} tags after ${tags.attempts} adds, contains jvm: ${"jvm" in tags}")פלט:
2 tags after 3 adds, contains jvm: trueמאפיינים מואצלים מעבירים את פעולות הקריאה והכתיבה של מאפיין לאובייקט אחר, שוב באמצעות by. lazy מפרק 2 הוא דוגמה למאציל כזה, וגם מפה יכולה לשמש כמאציל: כל מאפיין קורא את הערך מהערך המתאים ששמו כשם המאפיין:
class User(values: Map<String, Any?>) {
val name: String by values
val age: Int by values
}בתוך main:
val u = User(mapOf("name" to "Ada", "age" to 36))
println("${u.name} is ${u.age}")פלט:
Ada is 36אפשר לכתוב מאציל מאפיינים משלך: מחלקה עם operator fun getValue, ובנוסף setValue עבור var. Kotlin קוראת להם בכל קריאה וכתיבה של המאפיין. המאציל הזה מסיר רווחים מטקסט שמוקצה לו וסופר את השינויים:
import kotlin.reflect.KProperty
class Trimmed {
private var value = ""
var changes = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
value = newValue.trim()
changes++
}
}
class Form {
val nameField = Trimmed()
var name: String by nameField
}בתוך main:
val form = Form()
form.name = " Ada "
form.name = "Ada L. "
println("[${form.name}] after ${form.nameField.changes} changes")פלט:
[Ada L.] after 2 changesאתגר
קלהממשק Storage (עם put, get ו-keys) והמימוש שלו MapStorage מסופקים. כתבו שתי מחלקות מעטפת באמצעות האצלת מחלקה, שכל אחת מהן דורסת רק את put: LoggingStorage(inner) מתעדת put a=1 ברשימת log לקריאה בלבד שלה ומעבירה הלאה את הקריאה, ו-LimitedStorage(inner, max) מסרבת (מחזירה false) להוסיף מפתח חדש לאחר שקיימים max מפתחות, בעוד שעדכון של מפתחות קיימים עדיין מותר. כל השאר מועבר הלאה באמצעות by.
הקוד המסופק עוטף MapStorage ב-LoggingStorage, וכאשר השורה הראשונה היא מגבלה חיובית, עוטף את זה ב-LimitedStorage. הוא מריץ פקודות put a 1, get a ו-keys, ומדפיס ok, refused, את הערך או missing, ואת רשימת המפתחות, ולבסוף log: put a=1, ....
הקוד שלכם נמצא בקבצים LoggingStorage.kt, LimitedStorage.kt, Storage.kt ו-MapStorage.kt. Main.kt מכיל את קוד הקלט/פלט המסופק ואי אפשר לערוך אותו.
נסו בעצמכם
fun main() {
// קוד קלט/פלט שסופק: יש להשאיר אותו כפי שהוא
val input = generateSequence(::readLine).toList()
val base = MapStorage()
val logging = LoggingStorage(base)
val limit = input[0].toInt()
val storage: Storage = if (limit > 0) LimitedStorage(logging, limit) else logging
for (cmd in input.drop(1)) {
val p = cmd.split(" ")
when (p[0]) {
"put" -> println(if (storage.put(p[1], p[2])) "ok" else "refused")
"get" -> println(storage.get(p[1]) ?: "missing")
else -> println(storage.keys())
}
}
println("log: " + logging.log.joinToString(", ").ifEmpty { "empty" })
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1מחלקות ואובייקטים
עבודה עם קבציםהגדרת מחלקותבנאים ראשייםבלוקי init ואימות נתוניםבנאים משנייםסיכום – כרטיס ספרייהתרגלו בעצמכם: Kotlin אונליין