ה-codelab הזה הוא חלק מקורס Kotlin Bootcamp for Programmers. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. אם יש לכם ידע בנושא, יכול להיות שתוכלו לדלג על חלק מהקטעים. הקורס הזה מיועד למתכנתים שמכירים שפה מונחית-אובייקטים ורוצים ללמוד Kotlin.
מבוא
ב-codelab הזה נציג לכם מחלקות, פונקציות ושיטות גנריות, ונסביר איך הן פועלות ב-Kotlin.
במקום ליצור אפליקציה לדוגמה אחת, השיעורים בקורס הזה נועדו להרחיב את הידע שלכם, אבל הם גם עצמאיים למחצה, כך שתוכלו לדלג על חלקים שאתם מכירים. כדי לקשור בין הדוגמאות, רבות מהן מתבססות על נושא האקווריום. אם אתם רוצים לראות את הסיפור המלא של האקווריום, כדאי לעיין בקורס Kotlin Bootcamp for Programmers ב-Udacity.
מה שכדאי לדעת
- התחביר של פונקציות, מחלקות ושיטות ב-Kotlin
- איך יוצרים כיתה חדשה ב-IntelliJ IDEA ומריצים תוכנית
מה תלמדו
- איך עובדים עם מחלקות, שיטות ופונקציות גנריות
הפעולות שתבצעו:
- יצירת קטגוריה גנרית והוספת הגבלות
- יצירת סוגים של
inושלout - יצירת פונקציות גנריות, שיטות ופונקציות הרחבה
מבוא ל-generics
ב-Kotlin, כמו בשפות תכנות רבות, יש סוגים גנריים. סוג גנרי מאפשר להפוך מחלקה לגנרית, וכך להפוך אותה לגמישה הרבה יותר.
נניח שאתם מטמיעים מחלקה 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: יוצרים היררכיית סוגים
בשלב הזה יוצרים כמה כיתות לשימוש בשלב הבא. הסבר על יצירת מחלקת משנה מופיע ב-codelab קודם, אבל הנה סקירה קצרה.
- כדי שהדוגמה תהיה ברורה, יוצרים חבילה חדשה בתיקייה src וקוראים לה
generics. - בחבילה generics, יוצרים קובץ
Aquarium.ktחדש. כך תוכלו להגדיר מחדש דברים באמצעות אותם שמות בלי שיהיו התנגשויות, ולכן שאר הקוד שלכם בסדנת הקוד הזו ייכנס לקובץ הזה. - יצירת היררכיה של סוגי אספקת מים. כדי שיהיה אפשר ליצור מחלקת משנה, צריך להתחיל עם יצירת מחלקה
WaterSupplyמסוגopen. - מוסיפים פרמטר בוליאני
var, needsProcessing. הפעולה הזו יוצרת אוטומטית נכס שניתן לשינוי, יחד עם getter ו-setter. - יוצרים מחלקת משנה
TapWaterשמרחיבה אתWaterSupply, ומעבירים אתtrueל-needsProcessing, כי מי הברז מכילים תוספים שמזיקים לדגים. - ב-
TapWater, מגדירים פונקציה בשםaddChemicalCleaners()שקובעת את הערך שלneedsProcessingל-falseאחרי ניקוי המים. אפשר להגדיר את המאפייןneedsProcessingמ-TapWater, כי הואpublicכברירת מחדל ונגיש למחלקות משנה. הנה הקוד המלא.
package generics
open class WaterSupply(var needsProcessing: Boolean)
class TapWater : WaterSupply(true) {
fun addChemicalCleaners() {
needsProcessing = false
}
}- יוצרים עוד שני תת-מחלקות של
WaterSupply, בשםFishStoreWaterו-LakeWater. לא צריך לעבד אתFishStoreWater, אבל צריך לסנן אתLakeWaterבאמצעות השיטהfilter(). אחרי הסינון, אין צורך לעבד אותו שוב, ולכן בהגדרהfilter(), מגדירים אתneedsProcessing = false.
class FishStoreWater : WaterSupply(false)
class LakeWater : WaterSupply(true) {
fun filter() {
needsProcessing = false
}
}אם אתם צריכים מידע נוסף, כדאי לעיין בשיעור הקודם בנושא ירושה ב-Kotlin.
שלב 2: יצירת קטגוריה גנרית
בשלב הזה משנים את המחלקה Aquarium כדי לתמוך בסוגים שונים של אספקת מים.
- ב-Aquarium.kt, מגדירים מחלקה
Aquarium, עם<T>בסוגריים אחרי שם המחלקה. - הוספת מאפיין קבוע
waterSupplyמסוגTאלAquarium.
class Aquarium<T>(val waterSupply: T)- כותבים פונקציה בשם
genericsExample(). התג הזה לא שייך לכיתה, ולכן אפשר להוסיף אותו לרמה העליונה של הקובץ, כמו הפונקציהmain()או הגדרות הכיתה. בפונקציה, יוצריםAquariumומעבירים לוWaterSupply. הפרמטרwaterSupplyהוא פרמטר כללי, ולכן צריך לציין את הסוג שלו בתוך סוגריים זוויתיים<>.
fun genericsExample() {
val aquarium = Aquarium<TapWater>(TapWater())
}- ב-
genericsExample()הקוד שלכם יכול לגשת ל-waterSupplyשל האקווריום. מכיוון שהסוג שלו הואTapWater, אפשר להתקשר אלaddChemicalCleaners()בלי לבצע המרה של סוגים.
fun genericsExample() {
val aquarium = Aquarium<TapWater>(TapWater())
aquarium.waterSupply.addChemicalCleaners()
}- כשיוצרים את האובייקט
Aquarium, אפשר להסיר את הסוגריים הזוויתיים ואת מה שביניהם כי ל-Kotlin יש היסק סוגים. לכן, אין סיבה להשתמש ב-TapWaterפעמיים כשיוצרים את המופע. אפשר להסיק את הסוג מהארגומנט שלAquarium, אבל עדיין תתבצעAquariumמסוגTapWater.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
aquarium.waterSupply.addChemicalCleaners()
}- כדי לראות מה קורה, מדפיסים את
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}")
}- מוסיפים פונקציה
main()כדי לקרוא ל-genericsExample(), ואז מריצים את התוכנית ומתבוננים בתוצאה.
fun main() {
genericsExample()
}⇒ water needs processing: true water needs processing: false
שלב 3: הופכים את ההנחיה לספציפית יותר
המשמעות של 'כללי' היא שאפשר להעביר כמעט כל דבר, ולפעמים זה בעייתי. בשלב הזה מגדירים את המחלקה Aquarium בצורה ספציפית יותר לגבי מה שאפשר להכניס לתוכה.
- ב-
genericsExample(), יוצריםAquarium, מעבירים מחרוזת ל-waterSupply, ואז מדפיסים את המאפייןwaterSupplyשל האקווריום.
fun genericsExample() {
val aquarium2 = Aquarium("string")
println(aquarium2.waterSupply)
}- מריצים את התוכנית ומסתכלים על התוצאה.
⇒ string
התוצאה היא המחרוזת שהועברה, כי Aquarium לא מגביל את T.אפשר להעביר כל סוג, כולל String.
- ב-
genericsExample(), יוצרים עודAquariumומעבירים אתnullל-waterSupply. אם הערך שלwaterSupplyהוא null, הפונקציה מחזירה"waterSupply is null".
fun genericsExample() {
val aquarium3 = Aquarium(null)
if (aquarium3.waterSupply == null) {
println("waterSupply is null")
}
}- מריצים את התוכנית ומעיינים בתוצאה.
⇒ waterSupply is null
למה אפשר להעביר את הערך null כשיוצרים Aquarium? האפשרות הזו קיימת כי כברירת מחדל, T מייצג את הסוג Any? שניתן להגדיר לו ערך null, הסוג שנמצא בראש היררכיית הסוגים. התוכן הבא זהה למה שהקלדת קודם.
class Aquarium<T: Any?>(val waterSupply: T)- כדי לא לאפשר העברה של
null, צריך להגדיר אתTכסוגAnyבאופן מפורש, על ידי הסרת?אחריAny.
class Aquarium<T: Any>(val waterSupply: T)בהקשר הזה, Any נקרא מגבלה כללית. כלומר אפשר להעביר כל טיפוס ל-T כל עוד הוא לא null.
- מה שאתם באמת רוצים זה לוודא שאפשר להעביר רק
WaterSupply(או אחת ממחלקות המשנה שלו) ל-T. כדי להגדיר אילוץ גנרי ספציפי יותר, מחליפים אתAnyב-WaterSupply.
class Aquarium<T: WaterSupply>(val waterSupply: T)שלב 4: הוספת בדיקות נוספות
בשלב הזה נסביר על הפונקציה check() כדי לוודא שהקוד מתנהג כמו שציפיתם. הפונקציה check() היא פונקציה רגילה בספרייה ב-Kotlin. הפונקציה פועלת כהצהרה ותחזיר את השגיאה IllegalStateException אם הארגומנט שלה יחזיר את הערך false.
- מוסיפים שיטה
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() תזרוק חריגה.
- ב-
genericsExample(), מוסיפים קוד כדי ליצורAquariumעםLakeWater, ואז מוסיפים קצת מים.
fun genericsExample() {
val aquarium4 = Aquarium(LakeWater())
aquarium4.addWater()
}- מריצים את התוכנית ומקבלים חריגה, כי צריך לסנן את המים קודם.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first
at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)- מוסיפים שיחה כדי לסנן את המים לפני שמוסיפים אותם ל-
Aquarium. עכשיו, כשמריצים את התוכנית, לא מתקבלת חריגה.
fun genericsExample() {
val aquarium4 = Aquarium(LakeWater())
aquarium4.waterSupply.filter()
aquarium4.addWater()
}⇒ adding water from generics.LakeWater@880ec60
ההסבר שלמעלה כולל את עקרונות הבסיס של גנריות. במשימות הבאות מוסבר על עוד נושאים, אבל הרעיון החשוב הוא איך להצהיר על מחלקה גנרית עם אילוץ גנרי ואיך להשתמש בה.
במשימה הזו תלמדו על סוגי in ו-out עם גנריקה. סוג in הוא סוג שאפשר להעביר רק למחלקה, ולא להחזיר. סוג out הוא סוג שאפשר להחזיר רק ממחלקה.
אם תסתכלו על המחלקה Aquarium, תראו שהסוג הגנרי מוחזר רק כשמקבלים את המאפיין waterSupply. אין שיטות שמקבלות ערך מסוג T כפרמטר (חוץ מהגדרת הערך בבונה). ב-Kotlin אפשר להגדיר out סוגים בדיוק למקרה הזה, והמערכת יכולה להסיק מידע נוסף לגבי המקומות שבהם אפשר להשתמש בסוגים בצורה בטוחה. באופן דומה, אפשר להגדיר סוגים של in לסוגים גנריים שמועברים רק לשיטות, ולא מוחזרים. כך Kotlin יכולה לבצע בדיקות נוספות כדי לוודא שהקוד בטוח.
הסוגים in ו-out הם הנחיות למערכת הסוגים של Kotlin. הסבר על כל מערכת הסוגים חורג מהיקף ההדרכה הזו (מדובר במערכת מורכבת למדי), אבל הקומפיילר יסמן סוגים שלא סומנו כראוי ב-in וב-out, ולכן חשוב להכיר אותם.
שלב 1: הגדרת סוג של הוצאה
- בכיתה
Aquarium, משנים אתT: WaterSupplyלסוגout.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
...
}- באותו קובץ, מחוץ למחלקה, מכריזים על פונקציה
addItemTo()שמצפה ל-AquariumשלWaterSupply.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")- מתקשרים אל
addItemTo()מ-genericsExample()ומריצים את התוכנית.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
addItemTo(aquarium)
}⇒ item added
Kotlin יכולה לוודא ש-addItemTo() לא תבצע פעולות לא בטוחות מבחינת סוג עם הגנרי WaterSupply, כי היא מוצהרת כסוג out.
- אם מסירים את מילת המפתח
out, הקומפיילר יציג שגיאה כשקוראים ל-addItemTo(), כי Kotlin לא יכולה להבטיח שלא מתבצעות פעולות לא בטוחות עם הסוג.
שלב 2: הגדרת סוג ה-IN
הסוג in דומה לסוג out, אבל הוא מיועד לסוגים גנריים שמועברים רק לפונקציות ולא מוחזרים. אם תנסו להחזיר סוג in, תקבלו שגיאת קומפילציה. בדוגמה הזו תגדירו סוג in כחלק מממשק.
- ב-Aquarium.kt, מגדירים ממשק
Cleanerשמקבל פרמטר כלליTשמוגבל ל-WaterSupply. מכיוון שהיא משמשת רק כארגומנט ל-clean(), אפשר להגדיר אותה כפרמטרin.
interface Cleaner<in T: WaterSupply> {
fun clean(waterSupply: T)
}- כדי להשתמש בממשק
Cleaner, יוצרים מחלקהTapWaterCleanerשמטמיעה אתCleanerלניקויTapWaterעל ידי הוספת כימיקלים.
class TapWaterCleaner : Cleaner<TapWater> {
override fun clean(waterSupply: TapWater) = waterSupply.addChemicalCleaners()
}- בכיתה
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")
}
}- מעדכנים את קוד הדוגמה
genericsExample()כדי ליצורTapWaterCleaner, AquariumעםTapWater, ואז מוסיפים קצת מים באמצעות חומר הניקוי. הוא ישתמש בכלי לניקוי לפי הצורך.
fun genericsExample() {
val cleaner = TapWaterCleaner()
val aquarium = Aquarium(TapWater())
aquarium.addWater(cleaner)
}Kotlin תשתמש במידע על הסוגים in ו-out כדי לוודא שהקוד משתמש בגנריות בצורה בטוחה. קל לזכור את Out ו-in: אפשר להעביר החוצה סוגים של out כערכי החזרה, ואפשר להעביר פנימה סוגים של in כארגומנטים.

אם אתם רוצים להעמיק בנושא הבעיות שסוגי הקלט וסוגי הפלט פותרים, תוכלו לעיין במסמכי התיעוד.
במשימה הזו נסביר על פונקציות גנריות ומתי כדאי להשתמש בהן. בדרך כלל, מומלץ ליצור פונקציה גנרית בכל פעם שהפונקציה מקבלת ארגומנט של מחלקה עם סוג גנרי.
שלב 1: יוצרים פונקציה גנרית
- ב-generics/Aquarium.kt, יוצרים פונקציה
isWaterClean()שמקבלתAquarium. צריך לציין את הסוג הגנרי של הפרמטר. אפשרות אחת היא להשתמש ב-WaterSupply.
fun isWaterClean(aquarium: Aquarium<WaterSupply>) {
println("aquarium water is clean: ${aquarium.waterSupply.needsProcessing}")
}אבל המשמעות היא שצריך להגדיר ל-Aquarium פרמטר מסוג out כדי שאפשר יהיה לקרוא לו. לפעמים out או in מגבילים מדי כי צריך להשתמש בסוג גם לקלט וגם לפלט. אפשר להסיר את הדרישה out על ידי הפיכת הפונקציה לגנרית.
- כדי להפוך את הפונקציה לגנרית, מוסיפים סוגריים זוויתיים אחרי מילת המפתח
funעם סוג גנריTואילוצים כלשהם, במקרה הזהWaterSupply. משנים אתAquariumכך שיוגבל על ידיTבמקום על ידיWaterSupply.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}T הוא פרמטר מסוג isWaterClean() שמשמש לציון הסוג הגנרי של האקווריום. הדפוס הזה נפוץ מאוד, וכדאי להקדיש רגע כדי להבין אותו.
- כדי להפעיל את הפונקציה
isWaterClean(), מציינים את הסוג בסוגריים זוויתיים מיד אחרי שם הפונקציה ולפני הסוגריים.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
isWaterClean<TapWater>(aquarium)
}- בגלל היסק הסוג מהארגומנט
aquarium, אין צורך בסוג, ולכן צריך להסיר אותו. מריצים את התוכנית ומסתכלים על הפלט.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
isWaterClean(aquarium)
}⇒ aquarium water is clean: false
שלב 2: יוצרים שיטה גנרית עם סוג מוחשי
אפשר להשתמש בפונקציות גנריות גם למתודות, אפילו בכיתות שיש להן סוג גנרי משלהן. בשלב הזה מוסיפים ל-Aquarium שיטה כללית שבודקת אם יש לו סוג של WaterSupply.
- בכיתה
Aquarium, מכריזים על שיטה,hasWaterSupplyOfType(), שמקבלת פרמטר כלליR(כבר נעשה שימוש ב-T) שמוגבל ל-WaterSupply, ומחזירהtrueאםwaterSupplyהוא מסוגR. הפונקציה הזו דומה לפונקציה שהצהרתם עליה קודם, אבל היא נמצאת בתוך המחלקהAquarium.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R- שימו לב שהתוצאה הסופית
Rמודגשת בקו תחתון אדום. מעבירים את הסמן מעל הסמל כדי לראות מה השגיאה.
- כדי לבצע בדיקת
is, צריך לציין ב-Kotlin שהסוג הוא reified, כלומר אמיתי, ושאפשר להשתמש בו בפונקציה. כדי לעשות זאת, מוסיפיםinlineלפני מילת המפתחfunו-reifiedלפני סוג מילת המפתח הכלליתR.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is Rאחרי שסוג נהיה מוחשי, אפשר להשתמש בו כמו בסוג רגיל – כי הוא סוג אמיתי אחרי ההטמעה. כלומר, אפשר לבצע בדיקות is באמצעות הסוג.
אם לא משתמשים ב-reified כאן, הסוג לא יהיה מספיק 'אמיתי' כדי ש-Kotlin תאפשר בדיקות של is. הסיבה לכך היא שסוגים לא מוחשיים זמינים רק בזמן ההידור, ולא ניתן להשתמש בהם בזמן הריצה של התוכנית. הנושא הזה מוסבר בהרחבה בקטע הבא.
- מזינים את הערך
TapWaterכסוג. בדומה לקריאה לפונקציות גנריות, קוראים לשיטות גנריות באמצעות סוגריים זוויתיים עם הסוג אחרי שם הפונקציה. מריצים את התוכנית ומעיינים בתוצאה.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.hasWaterSupplyOfType<TapWater>()) // true
}⇒ true
שלב 3: יצירת פונקציות של תוסף
אפשר להשתמש בסוגים מוחשיים גם בפונקציות רגילות ובפונקציות הרחבה.
- מחוץ למחלקה
Aquarium, מגדירים פונקציית הרחבה ב-WaterSupplyבשםisOfType()שבודקת אםWaterSupplyשעבר הוא מסוג ספציפי, למשלTapWater.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T- מפעילים את פונקציית התוסף בדיוק כמו שמפעילים method.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.waterSupply.isOfType<TapWater>())
}⇒ true
עם פונקציות ההרחבה האלה, לא משנה איזה סוג של Aquarium זה (Aquarium או TowerTank או מחלקה משנית אחרת), כל עוד זה Aquarium. השימוש בתחביר של הקרנת כוכב הוא דרך נוחה לציין מגוון התאמות. כשמשתמשים בהטלת כוכב, Kotlin מוודאת שלא תבצעו פעולות לא בטוחות.
- כדי להשתמש בהטלת כוכבים, מזינים
<*>אחריAquarium. העברה שלhasWaterSupplyOfType()לפונקציית הרחבה, כי היא לא באמת חלק מליבת ה-API שלAquarium.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R- משנים את השיחה ל-
hasWaterSupplyOfType()ומריצים את התוכנית.
fun genericsExample() {
val aquarium = Aquarium(TapWater())
println(aquarium.hasWaterSupplyOfType<TapWater>())
}⇒ true
בדוגמה הקודמת, הייתם צריכים לסמן את הסוג הגנרי כ-reified ולהגדיר את הפונקציה כ-inline, כי Kotlin צריכה לדעת עליהם בזמן הריצה, ולא רק בזמן ההידור.
Kotlin משתמשת בכל הסוגים הגנריים רק בזמן ההידור. כך הקומפיילר יכול לוודא שאתם עושים הכול בצורה בטוחה. בזמן הריצה כל הסוגים הגנריים נמחקים, ולכן מוצגת הודעת השגיאה הקודמת לגבי בדיקת סוג שנמחק.
מסתבר שהקומפיילר יכול ליצור קוד נכון בלי לשמור את הסוגים הגנריים עד זמן הריצה. אבל זה כן אומר שלפעמים אתם עושים משהו, כמו is בדיקות של סוגים גנריים, שהקומפיילר לא יכול לתמוך בו. לכן, ב-Kotlin נוספו סוגים מוחשיים (reified) או אמיתיים.
אפשר לקרוא מידע נוסף על סוגים מוחשיים ומחיקת סוגים במסמכי התיעוד של Kotlin.
השיעור הזה התמקד בגנריות, שהן חשובות כדי שהקוד יהיה גמיש יותר וקל יותר לשימוש חוזר.
- כדאי ליצור מחלקות גנריות כדי שהקוד יהיה גמיש יותר.
- אפשר להוסיף אילוצים גנריים כדי להגביל את הסוגים שמשמשים עם גנריים.
- אפשר להשתמש בסוגים
inו-outעם גנריקות כדי לספק בדיקת סוגים טובה יותר, וכך להגביל את הסוגים שמועברים למחלקות או מוחזרים מהן. - יצירת פונקציות ושיטות גנריות לעבודה עם סוגים גנריים. לדוגמה:
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... } - שימוש בפונקציות כלליות של תוספים כדי להוסיף פונקציונליות לא בסיסית לכיתה.
- לפעמים יש צורך בסוגים מוחשיים בגלל מחיקת סוגים. סוגים מוחשיים, בניגוד לסוגים גנריים, נשמרים עד זמן הריצה.
- כדי לוודא שהקוד פועל כמו שצריך, משתמשים בפונקציה
check(). לדוגמה:check(!waterSupply.needsProcessing) { "water supply needs processing first" }
תיעוד של Kotlin
אם אתם רוצים לקבל מידע נוסף על נושא כלשהו בקורס הזה, או אם נתקעתם, https://kotlinlang.org הוא המקום הכי טוב להתחיל בו.
- Generics
- הגבלות כלליות
- Star-projections
- סוגים של
Inו-out - פרמטרים מוחשיים
- מחיקת סוגים
- פונקציית
check()
מדריכים ל-Kotlin
באתר https://try.kotlinlang.org יש הדרכות מפורטות שנקראות Kotlin Koans, מפרש מבוסס-אינטרנט וסט מלא של מסמכי עזר עם דוגמאות.
קורס של Udacity
כדי לצפות בקורס של Udacity בנושא הזה, אפשר לעבור אל Kotlin Bootcamp for Programmers.
IntelliJ IDEA
מסמכי התיעוד של IntelliJ IDEA זמינים באתר JetBrains.
בקטע הזה מפורטות אפשרויות למשימות ביתיות לתלמידים שעובדים על ה-Codelab הזה כחלק מקורס בהנחיית מדריך. המורה צריך:
- אם צריך, מקצים שיעורי בית.
- להסביר לתלמידים איך להגיש מטלות.
- בודקים את שיעורי הבית.
אנשי ההוראה יכולים להשתמש בהצעות האלה כמה שרוצים, ומומלץ להם להקצות כל שיעורי בית אחרים שהם חושבים שמתאימים.
אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.
עונים על השאלות
שאלה 1
מהי המוסכמה למתן שם לסוג גנרי?
▢ <Gen>
▢ <Generic>
▢ <T>
▢ <X>
שאלה 2
הגבלה על הסוגים שמותרים לסוג כללי נקראת:
▢ הגבלה גנרית
▢ אילוץ גנרי
▢ הבהרה
▢ מגבלה גנרית על סוג
שאלה 3
הגדרה של 'מוחשי':
▢ חושבה ההשפעה האמיתית של אובייקט על הביצוע.
▢ הוגדר אינדקס מוגבל של רשומות בכיתה.
▢ פרמטר הסוג הגנרי הפך לסוג אמיתי.
▢ הופעל אינדיקטור לשגיאה מרחוק.
עוברים לשיעור הבא:
סקירה כללית של הקורס, כולל קישורים ל-Codelabs אחרים, זמינה במאמר "Kotlin Bootcamp for Programmers: Welcome to the course".