Botlin Bootcamp for Programmers 5.2: Generic

מעבדת קוד זו היא חלק מקורס האתחול של מתכנתים בקוטלין. כדי להפיק את המקסימום מהקורס הזה, יש לפעול ברצף לפי קודי שיעור ה-Lab. בהתאם לידע שלכם, ייתכן שתוכלו לדלג על קטעים מסוימים. הקורס הזה מיועד למתכנתים בעלי שפה ממוקדת אובייקטים שרוצים ללמוד Kotlin.

מבוא

בשיעור Lab זה תקבלו קורסים, פונקציות ושיטות גנריות, וכן על האופן שבו הם עובדים ב-Kotlin.

במקום לפתח אפליקציה לדוגמה אחת, השיעורים בקורס הזה נועדו לפתח את הידע שלכם, אך אינם תלויים זה בזה כדי שתוכלו לעיין בסעיפים שאתם מכירים. כדי לקשר ביניהם, רבות מהדוגמאות משתמשות בעיצוב של אקווריום. אם אתם רוצים לראות את הסיפור המלא באקווריום, כדאי לנסות את הקורס של מתכנתים בקוטג'ין באוניברסיטה.

דברים שחשוב לדעת

  • התחביר, הפונקציות והשיטות של Kotlin
  • איך יוצרים כיתה חדשה ב- IntelliJ IDEA ומפעילים תוכנית?

מה תלמדו

  • איך לעבוד עם כיתות, שיטות ופונקציות גנריות

הפעולות שתבצעו:

  • יצירת מחלקה כללית והוספת אילוצים
  • יצירת in ו-out סוגים
  • יצירת פונקציות, שיטות ופונקציות כלליות

מבוא לגנריות

לקוטלין, כמו בשפות תכנות רבות, יש סוגים כלליים. סוג גנרי מאפשר להפוך את הכיתה לגנרית, וכך להפוך את הכיתה לגמישה יותר.

נניח שהטמעת שיעור אחד (MyList) שמכיל רשימת פריטים. ללא נתונים כלליים, עליך ליישם גרסה חדשה של MyList עבור כל סוג: אחת עבור Double, אחת עבור String, אחת עבור Fish. באופן כללי, אפשר ליצור רשימה גנרית, כך שהיא תוכל להכיל כל סוג של אובייקט. זה דומה ליצירת תו כללי לחיפוש שיתאים לסוגים רבים.

כדי להגדיר סוג גנרי, מוסיפים T בסוגריים מרובעים <T> אחרי שם הכיתה. (ניתן להשתמש באות אחרת או בשם ארוך יותר, אך הנוהג של סוג גנרי הוא T).

class MyList<T> {
    fun get(pos: Int): T {
        TODO("implement")
    }
    fun addItem(item: T) {}
}

אפשר להפנות את T כאילו הוא סוג רגיל. סוג ההחזרה של get() הוא T, והפרמטר אל addItem() הוא מסוג T. כמובן שרשימות גנריות שימושיות מאוד, ולכן הסיווג של List מובנה ב-Kotlin.

שלב 1: יצירת היררכיית סוג

בשלב זה תיצרו כיתות נוספות בשלב הבא. סיווג המשנה נכלל בשיעור קוד קודם, אבל לפניכם בדיקה קצרה.

  1. כדי שדוגמה לא תהיה עמוסה יותר, אפשר ליצור חבילה חדשה בקטע src ולקרוא לה generics.
  2. בחבילה הגנרית, יוצרים קובץ Aquarium.kt חדש. כך תוכל להגדיר מחדש פריטים באמצעות אותם שמות ללא התנגשויות, כך ששאר קוד הקוד עבור מעבד קוד זה יעבור לקובץ זה.
  3. יצירת היררכיה של סוגים של ספקי מים. תחילה עליך ליצור כיתה אחת (WaterSupply) כדי להעביר אותה למחלקה open.
  4. יש להוסיף פרמטר בוליאני var, needsProcessing. כך נוצר באופן אוטומטי נכס משתנה, יחד עם getter ו-Setter.
  5. יש ליצור מחלקת משנה TapWater שחורגת מ-WaterSupply ועוברת את true בשביל needsProcessing, כי מי ההקשה מכילים תוספים שלא מתאימים לדגים.
  6. במדיניות TapWater, מגדירים פונקציה בשם addChemicalCleaners() שמגדירה את הערך needsProcessing כ-false לאחר ניקוי המים. אפשר להגדיר את הנכס needsProcessing מ-TapWater כי הוא public כברירת מחדל ונגיש לכיתות משנה. הנה הקוד המלא.
package generics

open class WaterSupply(var needsProcessing: Boolean)

class TapWater : WaterSupply(true) {
   fun addChemicalCleaners() {
       needsProcessing = false
   }
}
  1. יצירת שתי קטגוריות משנה נוספות של WaterSupply, שנקראת FishStoreWater ו-LakeWater. אין צורך לעבד את FishStoreWater. עם זאת, צריך לסנן את LakeWater באמצעות השיטה filter(). לאחר הסינון, אין צורך לעבד אותו שוב, כך שב-filter() יש להגדיר את needsProcessing = false.
class FishStoreWater : WaterSupply(false)

class LakeWater : WaterSupply(true) {
   fun filter() {
       needsProcessing = false
   }
}

אם יש לך צורך במידע נוסף, כדאי לעיין בשיעור הקודם בנושא ירושה בקוטלין.

שלב 2: יצירת כיתה גנרית

בשלב זה, אתם משנים את המחלקה Aquarium כדי לתמוך בסוגים שונים של ציוד מים.

  1. ב-Aquaium.kt, מגדירים כיתה Aquarium, כאשר <T> מוסיפים סוגריים אחרי שם הכיתה.
  2. צריך להוסיף נכס קבוע מסוג waterSupply מסוג T ל-Aquarium.
class Aquarium<T>(val waterSupply: T)
  1. צריך לכתוב פונקציה בשם genericsExample(). החלק הזה לא שייך לכיתה, ולכן הוא יכול להיות ברמה העליונה של הקובץ, כמו הפונקציה main() או הגדרות הכיתה. בפונקציה, מבצעים Aquarium ומעבירים אותו ל-WaterSupply. מכיוון שהפרמטר waterSupply הוא כללי, צריך לציין את הסוג בסוגריים מרובעים <>.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
}
  1. ב-genericsExample() הקוד שלך יכול לגשת אל האקווריום waterSupply. מאחר שהוא מסוג TapWater, אפשר להתקשר ל-addChemicalCleaners() בלי להעביר מכל סוג שהוא.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. כשיוצרים את האובייקט Aquarium, אפשר להסיר את סוגריים הזווית ואת מה שביניהם כי ל-Kotlin יש הסקת סוג. לכן אין סיבה לומר TapWater פעמיים כשיוצרים את המכונה. ניתן להסיק את הסוג הזה על-ידי הארגומנט Aquarium. הוא עדיין ייצור Aquarium מסוג TapWater.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. כדי לראות מה קורה, מדפיסים את needsProcessing לפני ואחרי השיחה ל-addChemicalCleaners(). לפניכם הפונקציה שהושלמה.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
    aquarium.waterSupply.addChemicalCleaners()
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
}
  1. צריך להוסיף פונקציה main() כדי להתקשר אל genericsExample(), להפעיל את התוכנית ולצפות בתוצאה.
fun main() {
    genericsExample()
}
⇒ water needs processing: true
water needs processing: false

שלב 3: הגדרה ספציפית יותר

המונח 'כללי' פירושו שניתן להעביר כמעט כל דבר, ולפעמים זו בעיה. בשלב זה, הופכים את הכיתה ל-Aquarium ספציפית יותר במה שאפשר לכלול בה.

  1. באפליקציה genericsExample(), יש ליצור Aquarium, להעביר מחרוזת עבור waterSupply ולהדפיס את נכס האקווריום waterSupply.
fun genericsExample() {
    val aquarium2 = Aquarium("string")
    println(aquarium2.waterSupply)
}
  1. מפעילים את התוכנית וצופים בתוצאה.
⇒ string

התוצאה היא המחרוזת שהעברת, כי Aquarium לא מגבילה את T. בשום סוג, כולל String.

  1. ב-genericsExample(), יש ליצור עוד Aquarium, להעביר את ה-null בשביל waterSupply. אם waterSupply הוא null, יש להדפיס "waterSupply is null".
fun genericsExample() {
    val aquarium3 = Aquarium(null)
    if (aquarium3.waterSupply == null) {
        println("waterSupply is null")
    }
}
  1. מפעילים את התוכנית ומעיינים בתוצאה.
⇒ waterSupply is null

למה כדאי לעבור את null כשיוצרים Aquarium? ניתן לעשות זאת, כי כברירת מחדל, T מייצג את הסוג Any? שניתן לביטול, הסוג שבראש היררכיית הסוגים. הטקסט הבא זהה למה שהקלדת קודם לכן.

class Aquarium<T: Any?>(val waterSupply: T)
  1. כדי לא להעביר את null, יש לבצע הסרה מפורשת של T מסוג Any, על ידי הסרת ? לאחר Any.
class Aquarium<T: Any>(val waterSupply: T)

בהקשר הזה, Any נקרא מגבלה גנרית. כלומר, אפשר להעביר כל סוג עבור T, כל עוד הוא לא מוגדר כ-null.

  1. מה שחשוב לך הוא לוודא שניתן להעביר רק WaterSupply (או אחת מסיווגי המשנה שלו) עבור T. כדי להגדיר אילוץ כללי ספציפי יותר, אפשר להחליף את Any ב-WaterSupply.
class Aquarium<T: WaterSupply>(val waterSupply: T)

שלב 4: מוסיפים עוד בדיקות

בשלב זה תלמדו על הפונקציה check() כדי לוודא שהקוד שלכם פועל כמצופה. הפונקציה check() היא פונקציה רגילה של ספרייה בקוטלין. היא משמשת כהצהרה, ומעלמת IllegalStateException אם הארגומנט שלה מעריך את false.

  1. צריך להוסיף שיטה אחת (addWater()) לכיתה Aquarium כדי להוסיף מים, עם check() שמוודאים שאין צורך לעבד את המים קודם.
class Aquarium<T: WaterSupply>(val waterSupply: T) {
    fun addWater() {
        check(!waterSupply.needsProcessing) { "water supply needs processing first" }
        println("adding water from $waterSupply")
    }    
}

במקרה כזה, אם התנאי needsProcessing הוא True, check() יחריג אותו.

  1. ב-genericsExample(), יש להוסיף קוד כדי ליצור Aquarium עם LakeWater, ואז להוסיף אליו מים.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.addWater()
}
  1. מפעילים את התוכנית ומקבלים חריג, כי תחילה צריך לסנן את המים.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first
        at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)
  1. יש להוסיף שיחה כדי לסנן את המים לפני שמוסיפים אותה אל Aquarium. עכשיו כשמריצים את התוכנית, לא יוצאים מהכלל.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.waterSupply.filter()
    aquarium4.addWater()
}
⇒ adding water from generics.LakeWater@880ec60

הנושאים שלמעלה מכסים את העקרונות הגנריים. המשימות הבאות עוסקות יותר, אבל הרעיון החשוב הוא איך להצהיר על סיווג גנרי ולהשתמש בו, אם יש מגבלה כללית.

במשימה זו, כדאי לקרוא על הסוגים הכלליים ומחוץ להם, באופן כללי. סוג של in הוא סוג שאפשר להעביר רק לכיתה, ולא להחזיר. סוג של out הוא סוג שאפשר להחזיר רק מכיתה.

אם בודקים את הכיתה Aquarium, אפשר לראות שהסוג הגנרי מוחזר רק כשמקבלים את הנכס waterSupply. אין שיטות שמשתמשות בערך מסוג T כפרמטר (מלבד הגדרתו בבנייה). Kotlin מאפשר לך להגדיר out סוגים בדיוק למקרה הזה, והוא יכול להסיק מידע נוסף לגבי המקום שבו הסוגים בטוחים לשימוש. באופן דומה, ניתן להגדיר in סוגים של סוגים כלליים בלבד המועברים בשיטות בלבד, ואינם מוחזרים. ההרשאה הזו מאפשרת ל-Kotlin לבצע בדיקות נוספות לאבטחת קוד.

הסוגים in ו-out הם הוראות למערכות מסוג Kotlin's. מתן הסברים על כך שכל סוג הפעילות נמצא מחוץ למסגרת המחנה הזה (הוא מעורב במידה מסוימת). עם זאת, המהדר יסמן סוגים שאינם מסומנים כ-in וגם כ-out, לכן חשוב לדעת עליהם.

שלב 1: מגדירים סוג

  1. במחלקה Aquarium, משנים את T: WaterSupply לסוג out.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    ...
}
  1. באותו קובץ, מחוץ לכיתה, צריך להצהיר על פונקציה addItemTo() שצפויה להיות Aquarium מתוך WaterSupply.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")
  1. התקשרות אל addItemTo() מ-genericsExample() והפעלה של התוכנית.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    addItemTo(aquarium)
}
⇒ item added

קוטלין יכול לוודא שaddItemTo() לא יבצע שום פעולה לא בטוחה עם WaterSupply הכללי, מפני שהוא מוגדר כסוג out.

  1. אם תסירו את מילת המפתח out, המהדר יציין שגיאה בעת התקשרות ל-addItemTo(), מפני ש-Kotlin לא יכול לוודא שאינכם עושים פעולה לא בטוחה מסוג זה.

שלב 2: הגדרה של סוג

הסוג של in דומה לסוג out, אבל במקרה של סוגים כלליים שמועברים רק בפונקציות, לא מוחזרים. אם תנסה להחזיר סוג in, תקבל שגיאת מהדר. בדוגמה הזו מגדירים סוג in כחלק מממשק.

  1. ב-Aquaium.kt, מגדירים ממשק Cleaner שמשתמש ב-T' שהוא מוגבל ל-WaterSupply. הוא משמש רק כארגומנט ל-clean(), ולכן אפשר להגדיר אותו כפרמטר in.
interface Cleaner<in T: WaterSupply> {
    fun clean(waterSupply: T)
}
  1. כדי להשתמש בממשק Cleaner, צריך ליצור מחלקה TapWaterCleaner שמטמיעה את Cleaner לניקוי TapWater על-ידי הוספת כימיקלים.
class TapWaterCleaner : Cleaner<TapWater> {
    override fun clean(waterSupply: TapWater) =   waterSupply.addChemicalCleaners()
}
  1. בכיתה Aquarium, יש לעדכן את addWater() כדי לקבל Cleaner מסוג T, ולנקות את המים לפני הוספתו.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    fun addWater(cleaner: Cleaner<T>) {
        if (waterSupply.needsProcessing) {
            cleaner.clean(waterSupply)
        }
        println("water added")
    }
}
  1. יש לעדכן את הקוד לדוגמה של genericsExample() וליצור TapWaterCleaner, Aquarium עם TapWater, ואז להוסיף מים באמצעות חומרי הניקוי. הוא ישתמש בחומרי ניקוי לפי הצורך.
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

Kotlin ישתמש בפרטי הסוג in ו-out כדי לוודא שהקוד שלך משתמש בצורה הכללית. קל לזכור את Out ואת in: ניתן להעביר out סוגים כערכי החזרה, ואת in הסוגים אפשר להעביר פנימה כארגומנטים.

כדי לקבל מידע נוסף על פתרון בעיות בסוגים ובסוגים שונים, אפשר לקרוא את התיעוד לעומק.

במשימה הזו תלמדו על פונקציות כלליות ומתי להשתמש בהן. בדרך כלל, כדאי ליצור פונקציה גנרית בכל פעם שהפונקציה מקבלת ארגומנט של מחלקה מסוג מסוים.

שלב 1: יצירת פונקציה כללית

  1. ב-generics/Aquaium.kt, יוצרים פונקציה isWaterClean() שמקבלת Aquarium. עליך לציין את הסוג הגנרי של הפרמטר. אפשרות אחת היא להשתמש במאפיין WaterSupply.
fun isWaterClean(aquarium: Aquarium<WaterSupply>) {
   println("aquarium water is clean: ${aquarium.waterSupply.needsProcessing}")
}

עם זאת, המשמעות היא ש-Aquarium חייב לכלול פרמטר מסוג out כדי לבצע את הפעולה הזו. לפעמים out או in מגבילות מדי כי צריך להשתמש בסוג גם לקלט וגם לפלט. אפשר להסיר את הדרישה out על ידי הגדרת הפונקציה כגנרית.

  1. כדי להפוך את הפונקציה לגנרית, יש להציב אחרי מילת המפתח fun את סוגריים זוויתיים עם סוג גנרי T ועם אילוצים אחרים, במקרה הזה, WaterSupply. יש לשנות את Aquarium כך שההגבלה תהיה על ידי T במקום על ידי WaterSupply.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
   println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}

הפרמטר T הוא פרמטר סוג שנועד ל-isWaterClean() המשמש לציון הסוג הגנרי של האקווריום. הדפוס הזה נפוץ מאוד, ומומלץ להקדיש כמה רגעים כדי לעבור אותו.

  1. צריך לקרוא לפונקציה isWaterClean() על ידי ציון הסוג בסוגריים מרובעים מיד אחרי שם הפונקציה ולפני הסוגריים.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean<TapWater>(aquarium)
}
  1. בשל המסקנות של הסוג מהארגומנט aquarium, אין צורך בסוג הזה, לכן יש להסיר אותו. מפעילים את התוכנית וצופים בפלט.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean(aquarium)
}
⇒ aquarium water is clean: false

שלב 2: יוצרים שיטה כללית עם סוג של תיקון

אפשר להשתמש בפונקציות גנריות גם בשיטות, גם בכיתות מהסוג שלהן. בשלב זה מוסיפים שיטה כללית ל-Aquarium שבודקת אם יש בה סוג של WaterSupply.

  1. בכיתה Aquarium, מצהירים על שיטה hasWaterSupplyOfType() , שמשתמשת בפרמטר כללי R (T כבר בשימוש) מוגבלת ל-WaterSupply ומחזירה true אם waterSupply הוא מסוג R. היא דומה לפונקציה שהצהרת עליה קודם, אבל בתוך הכיתה Aquarium.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
  1. חשוב לשים לב שהR הסופי מודגש בקו תחתון אדום. מציבים את הסמן מעליה כדי לראות מהי השגיאה.
  2. כדי לבצע בדיקה של is, עליך לומר ל-Kotlin שהסוג reled או סוג אמיתי, ושאפשר להשתמש בו בפונקציה. כדי לעשות זאת, צריך להציג את inline לפני מילת המפתח fun, ואת המילה reified לפני הסוג הגנרי R.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R

לאחר תיקון של סוג, אפשר להשתמש בו כמו סוג רגיל, כי הוא מסוג אמיתי אחרי ההטבעה. כלומר, אפשר לבצע is בדיקות באמצעות הסוג.

אם לא משתמשים כאן ב-reified, סוג זה לא יהיה "real" כך ש-Kotlin לא יתיר בדיקות של is. הסיבה לכך היא שסוגים שלא תוקנו זמינים רק בזמן ההידור, ואי אפשר להשתמש בהם בזמן הריצה בתוכנית שלכם. מאמר זה מתואר בקטע הבא.

  1. מעבר ל-TapWater כסוג. כמו שמתקשרים לפונקציות גנריות, אפשר להשתמש בשיטות גנריות באמצעות סוגריים זוויתיים מהסוג שמופיע אחרי שם הפונקציה. מפעילים את התוכנית ומעיינים בתוצאה.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())   // true
}
⇒ true

שלב 3: יוצרים פונקציות של תוספים

אפשר להשתמש בסוגים מחודשים גם לפונקציות רגילות ולפונקציות תוספים.

  1. מחוץ לכיתה Aquarium, צריך להגדיר פונקציית תוסף ב-WaterSupply שנקראת isOfType() כדי לבדוק אם ה-WaterSupply הוא מסוג ספציפי. לדוגמה: TapWater.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T
  1. צריך לקרוא לפונקציה של התוסף בדיוק כמו בשיטה.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.waterSupply.isOfType<TapWater>())  
}
⇒ true

עם פונקציות התוסף האלה, לא משנה מה סוג הAquarium שלהן (Aquarium או TowerTank או סוג משנה אחר), כל עוד הוא Aquarium. התחביר של star הקרנה הוא דרך נוחה לציין מגוון של התאמות. כשאתם משתמשים בתחזית כוכבים, קוטלין יוודא שגם אתם לא עושים דבר לא בטוח.

  1. כדי להשתמש בתחזית כוכבים, יש להוסיף <*> אחרי Aquarium. יש להעביר את hasWaterSupplyOfType() כפונקציית תוסף, מפני שהוא לא באמת חלק מה-API הראשי של Aquarium.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R
  1. משנים את השיחה ל-hasWaterSupplyOfType() ומפעילים את התוכנית.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())
}
⇒ true

בדוגמה הקודמת, היה עליך לסמן את הסוג הגנרי בתור reified ולהגדיר את הפונקציה inline, מפני שקוטלין צריך לדעת עליהם בזמן הריצה, ולא רק זמן הביצוע.

כל הסוגים הכלליים משמשים רק בעת ההידור של קוטלין. כך המהדר יכול לוודא שאתם עושים הכול בצורה בטוחה. בזמן ריצה כל הסוגים הגנריים נמחקים, ולכן הודעת השגיאה הקודמת לגבי בדיקת סוג שנמחק תימחק.

לאחר מכן, המהדר יכול ליצור קוד נכון בלי לשמור את הסוגים הגנריים עד לזמן הריצה. אבל לפעמים המשמעות היא שאתם עושים פעולה כלשהי, כמו is, שבודקת את הסוגים הגנריים והמהדר לא יכול לתמוך בהם. זו הסיבה לכך שקוטלין הוסיף סוגים מאושרים או אמיתיים.

אפשר לקרוא מידע נוסף על סוגים מאומתים ומחיקת סוגים בתיעוד של Kotlin.

השיעור הזה מתמקד בתכנים גנריים, שהם חשובים יותר להפוך את הקוד לגמיש יותר ונוח יותר לשימוש חוזר.

  • יצירת סיווגים כלליים שהופכים את הקוד לגמיש יותר.
  • הוסיפו אילוצים כלליים כדי להגביל את הסוגים שנעשה בהם שימוש באופן כללי.
  • כדאי להשתמש בסוגים in ו-out באופן כללי כדי לספק בדיקת סוגים טובה יותר כדי להגביל את סוגי הכיתות שצריך להחזיר או להחזיר מהכיתות.
  • יש ליצור פונקציות ושיטות כלליות כדי לעבוד עם סוגים כלליים. למשל:
    fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... }
  • שימוש בפונקציות גנריות כדי להוסיף לכיתה פונקציות שאינן ליבה.
  • מדי פעם, יש צורך לבצע תיקון של סוגי הסיווג. סוגים שעברו עדכון, בשונה מסוגים כלליים, נמשכים לזמן ריצה.
  • יש להשתמש בפונקציה check() כדי לוודא שהקוד פועל כמצופה. למשל:
    check(!waterSupply.needsProcessing) { "water supply needs processing first" }

התיעוד של Kotlin

אם אתם רוצים לקבל מידע נוסף על כל נושא בקורס הזה, או אם נתקעים, https://kotlinlang.org היא נקודת ההתחלה הטובה ביותר.

מדריכים בקוטלין

האתר https://try.kotlinlang.org כולל מדריכים עשירים בשם Kotlin Koans, מתורגמן מבוסס-אינטרנט וסדרה מלאה של מסמכי עזר עם דוגמאות.

קורס של Udacity

כדי להציג את הקורס של Udacity בנושא זה, ניתן לעיין ב-Kotlin Bootcamp for Programmers.

IntelliJ IDEA

ניתן למצוא תיעוד עבור IntelliJ IDEA באתר JetBrains.

בקטע הזה מפורטות מטלות שיעורי בית אפשריות לתלמידים שעובדים עם קוד Lab הזה, במסגרת קורס בהדרכת מורה. למורה יש אפשרות לבצע את הפעולות הבאות:

  • אם צריך, מקצים שיעורי בית.
  • ספרו לתלמידים איך מגישים מטלות בשיעורי בית.
  • לתת ציונים למטלות שיעורי הבית.

המורים יכולים להשתמש בהצעות האלה כמה שפחות, ומומלץ להקצות להן כל שיעורי בית שדעתם מתאימה להם.

אם אתם עובדים בעצמכם על שיעור הקוד הזה, אתם מוזמנים להשתמש במטלות שיעורי הבית האלה כדי לבחון את הידע שלכם.

עונים על השאלות האלה

שאלה 1

על איזו מוסכמה למתן שם לסוג גנרי?

<Gen>

<Generic>

<T>

<X>

שאלה 2

הגבלה על סוגים מהסוגים המותרים נקראת:

הוספה של הגבלה כללית

הוספה של אילוץ כללי

▬ הבחנה

הוספה של מגבלה מסוג כללי כללית

שאלה 3

המשמעות של חידוד:

▸ מחושבים ההשפעה בפועל של אובייקט בפועל.

הוספה של מדד עם ערך מוגבל הוגדרה לכיתה.

הוספה של פרמטר הסוג הכללי הפך לסוג אמיתי.

הוספה של אינדיקטור שגיאה מרחוק הופעל.

המשך לשיעור הבא: 6. מניפולציה פונקציונלית

סקירה כללית של הקורס, כולל קישורים למעבדות קוד אחרות, זמינה במאמר "Kotlin Bootcamp for Programmers: ברוך הבא לקורס."