ה-codelab הזה הוא חלק מקורס Kotlin Bootcamp for Programmers. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. אם יש לכם ידע בנושא, יכול להיות שתוכלו לדלג על חלק מהקטעים. הקורס הזה מיועד למתכנתים שמכירים שפה מונחית-אובייקטים ורוצים ללמוד Kotlin.
מבוא
ב-codelab הזה יוצרים תוכנית Kotlin ולומדים על מחלקות ואובייקטים ב-Kotlin. אם אתם מכירים שפה אחרת מונחית-עצמים, חלק גדול מהתוכן הזה יהיה מוכר לכם, אבל יש הבדלים חשובים בין Kotlin לשפות אחרות, שמטרתם לצמצם את כמות הקוד שאתם צריכים לכתוב. תלמדו גם על מחלקות מופשטות ועל העברת הרשאות לממשק.
במקום ליצור אפליקציה לדוגמה אחת, השיעורים בקורס הזה נועדו להרחיב את הידע שלכם, אבל הם גם עצמאיים למחצה, כך שתוכלו לדלג על חלקים שאתם מכירים. כדי לקשור בין הדוגמאות, רבות מהן מתבססות על נושא האקווריום. אם אתם רוצים לראות את הסיפור המלא של האקווריום, כדאי לעיין בקורס Kotlin Bootcamp for Programmers ב-Udacity.
מה שכדאי לדעת
- היסודות של Kotlin, כולל סוגים, אופרטורים ולולאות
- תחביר הפונקציות של Kotlin
- היסודות של תכנות מונחה-עצמים
- היסודות של סביבת פיתוח משולבת (IDE) כמו IntelliJ IDEA או Android Studio
מה תלמדו
- איך יוצרים מחלקות וניגשים למאפיינים ב-Kotlin
- איך יוצרים ומשתמשים בבוני מחלקות ב-Kotlin
- איך יוצרים מחלקת משנה ואיך פועל העיקרון של ירושה
- מידע על מחלקות מופשטות, ממשקים והעברת הרשאה לממשק
- איך יוצרים סיווגי נתונים ומשתמשים בהם
- איך משתמשים בסינגלטונים, ב-enums ובמחלקות אטומות
הפעולות שתבצעו:
- יצירת כיתה עם מאפיינים
- יצירת בנאי למחלקה
- יצירת מחלקת משנה
- לבדוק דוגמאות של מחלקות מופשטות וממשקים
- יצירת מחלקת נתונים פשוטה
- מידע על סינגלטונים, סוגי enum ומחלקות sealed
אתם כבר אמורים להכיר את מונחי התכנות הבאים:
- Classes הם תוכניות לאובייקטים. לדוגמה, מחלקה
Aquarium
היא תוכנית ליצירת אובייקט של אקווריום. - אובייקטים הם מופעים של מחלקות; אובייקט של אקווריום הוא
Aquarium
בפועל. - מאפיינים הם מאפיינים של מחלקות, כמו האורך, הרוחב והגובה של
Aquarium
. - שיטות, שנקראות גם פונקציות חברות, הן הפונקציונליות של המחלקה. השיטות הן הפעולות שאפשר לבצע באובייקט. לדוגמה, אפשר
fillWithWater()
אובייקטAquarium
. - ממשק הוא מפרט שמחלקה יכולה להטמיע. לדוגמה, ניקוי הוא פעולה שמשותפת לאובייקטים שונים, לא רק לאקווריומים, והיא מתבצעת בדרך כלל בצורה דומה לאובייקטים שונים. לכן יכול להיות לכם ממשק בשם
Clean
שמגדיר שיטה בשםclean()
. המחלקהAquarium
יכולה להטמיע את הממשקClean
כדי לנקות את האקווריום עם ספוג רך. - חבילות הן דרך לקבץ קוד שקשור אחד לשני כדי לשמור על סדר, או כדי ליצור ספרייה של קוד. אחרי שיוצרים חבילה, אפשר לייבא את התוכן שלה לקובץ אחר ולעשות שימוש חוזר בקוד ובמחלקות שבה.
במשימה הזו, יוצרים חבילה חדשה וכיתה עם כמה מאפיינים ושיטה.
שלב 1: יצירת חבילה
חבילות יכולות לעזור לכם לשמור על ארגון הקוד.
- בחלונית Project, מתחת לפרויקט Hello Kotlin, לוחצים לחיצה ימנית על התיקייה src.
- בוחרים באפשרות חדש > חבילה ונותנים לה את השם
example.myapp
.
שלב 2: יצירת מחלקה עם מאפיינים
מגדירים מחלקות באמצעות מילת המפתח class
, וכמקובל, שמות המחלקות מתחילים באות גדולה.
- לוחצים לחיצה ימנית על חבילת example.myapp.
- בוחרים באפשרות New > Kotlin File / Class (חדש > קובץ או מחלקה של Kotlin).
- בקטע סוג, בוחרים באפשרות כיתה ונותנים שם לכיתה
Aquarium
. IntelliJ IDEA כולל את שם החבילה בקובץ ויוצר בשבילכם מחלקה ריקה בשםAquarium
. - בתוך המחלקה
Aquarium
, מגדירים ומאתחלים מאפיינים שלvar
לרוחב, לגובה ולאורך (בסנטימטרים). מאחלים את הנכסים עם ערכי ברירת מחדל.
package example.myapp
class Aquarium {
var width: Int = 20
var height: Int = 40
var length: Int = 100
}
מתחת לפני השטח, Kotlin יוצרת באופן אוטומטי פונקציות getter ו-setter למאפיינים שהגדרתם במחלקה Aquarium
, כך שתוכלו לגשת למאפיינים ישירות, למשל myAquarium.length
.
שלב 3: יוצרים פונקציה main()
יוצרים קובץ חדש בשם main.kt
כדי להכיל את הפונקציה main()
.
- בחלונית Project שמימין, לוחצים לחיצה ימנית על החבילה example.myapp.
- בוחרים באפשרות New > Kotlin File / Class (חדש > קובץ או מחלקה של Kotlin).
- בתפריט הנפתח סוג, משאירים את הבחירה קובץ ונותנים לקובץ את השם
main.kt
. IntelliJ IDEA כולל את שם החבילה, אבל לא כולל הגדרת מחלקה לקובץ. - מגדירים פונקציה
buildAquarium()
ובתוכה יוצרים מופע שלAquarium
. כדי ליצור מופע, מפנים אל המחלקה כאילו היא פונקציה,Aquarium()
. הפעולה הזו קוראת לבנאי של המחלקה ויוצרת מופע של המחלקהAquarium
, בדומה לשימוש ב-new
בשפות אחרות. - מגדירים פונקציה
main()
וקוראים לפונקציהbuildAquarium()
.
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium()
}
fun main() {
buildAquarium()
}
שלב 4: מוסיפים שיטה
- במחלקת
Aquarium
, מוסיפים שיטה להדפסת מאפייני המימד של האקווריום.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
}
- ב-
main.kt
, ב-buildAquarium()
, מפעילים את השיטהprintSize()
ב-myAquarium
.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
}
- מריצים את התוכנית בלחיצה על המשולש הירוק לצד הפונקציה
main()
. בודקים את התוצאה.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm
- ב-
buildAquarium()
, מוסיפים קוד כדי להגדיר את הגובה ל-60 ולהדפיס את מאפייני המאפיין ששונו.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
myAquarium.height = 60
myAquarium.printSize()
}
- מריצים את התוכנית ומסתכלים על הפלט.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 100 cm Height: 60 cm
במשימה הזו יוצרים בנאי למחלקה וממשיכים לעבוד עם מאפיינים.
שלב 1: יצירת בנאי
בשלב הזה מוסיפים בנאי למחלקה Aquarium
שיצרתם במשימה הראשונה. בדוגמה הקודמת, כל מופע של Aquarium
נוצר עם אותם מאפיינים. אפשר לשנות את המידות אחרי שיוצרים את הציור על ידי הגדרת המאפיינים, אבל יהיה פשוט יותר ליצור אותו בגודל הנכון מלכתחילה.
בשפות תכנות מסוימות, הקונסטרוקטור מוגדר על ידי יצירת שיטה בתוך המחלקה עם אותו שם כמו שם המחלקה. ב-Kotlin, מגדירים את ה-constructor ישירות בהצהרת המחלקה עצמה, ומציינים את הפרמטרים בתוך סוגריים כאילו המחלקה הייתה שיטה. בדומה לפונקציות ב-Kotlin, הפרמטרים האלה יכולים לכלול ערכי ברירת מחדל.
- במחלקת
Aquarium
שיצרתם קודם, משנים את הגדרת המחלקה כך שתכלול שלושה פרמטרים של בנאי עם ערכי ברירת מחדל ל-length
, ל-width
ול-height
, ומקצים אותם למאפיינים המתאימים.
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
// Dimensions in cm
var length: Int = length
var width: Int = width
var height: Int = height
...
}
- ב-Kotlin, הדרך הקומפקטית יותר היא להגדיר את המאפיינים ישירות באמצעות בנאי, באמצעות
var
אוval
. בנוסף, Kotlin יוצרת את הפונקציות לקבלת ערכים (getters) ולשינוי ערכים (setters) באופן אוטומטי. אחר כך תוכלו להסיר את הגדרות המאפיינים בגוף הכיתה.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
- כשיוצרים אובייקט
Aquarium
באמצעות בנאי כזה, אפשר לא לציין ארגומנטים ולקבל את ערכי ברירת המחדל, או לציין רק חלק מהם, או לציין את כולם וליצור אובייקטAquarium
בגודל מותאם אישית לחלוטין. בפונקציהbuildAquarium()
, נסו ליצור אובייקטAquarium
בדרכים שונות באמצעות פרמטרים בעלי שם.
fun buildAquarium() {
val aquarium1 = Aquarium()
aquarium1.printSize()
// default height and length
val aquarium2 = Aquarium(width = 25)
aquarium2.printSize()
// default width
val aquarium3 = Aquarium(height = 35, length = 110)
aquarium3.printSize()
// everything custom
val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
aquarium4.printSize()
}
- מריצים את התוכנית ומעיינים בפלט.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 25 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 110 cm Height: 35 cm Width: 25 cm Length: 110 cm Height: 35 cm
שימו לב שלא הייתם צריכים להעמיס יתר על המידה על ה-constructor ולכתוב גרסה שונה לכל אחד מהמקרים האלה (ועוד כמה גרסאות לשאר השילובים). Kotlin יוצרת את מה שצריך מתוך ערכי ברירת המחדל והפרמטרים בעלי השם.
שלב 2: מוסיפים בלוקים של init
הקונסטרוקטורים לדוגמה שלמעלה רק מצהירים על מאפיינים ומקצים להם את הערך של ביטוי. אם הקונסטרוקטור צריך עוד קוד אתחול, אפשר להציב אותו באחד או יותר מבלוקים של init
. בשלב הזה מוסיפים כמה בלוקים של init
לכיתה Aquarium
.
- במחלקת
Aquarium
, מוסיפים בלוקinit
כדי להדפיס שהאובייקט עובר אתחול, ובלוק שני כדי להדפיס את הנפח בליטרים.
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
init {
println("aquarium initializing")
}
init {
// 1 liter = 1000 cm^3
println("Volume: ${width * length * height / 1000} l")
}
}
- מריצים את התוכנית ומעיינים בפלט.
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm
שימו לב שבלוקי init
מבוצעים בסדר שבו הם מופיעים בהגדרת המחלקה, וכולם מבוצעים כשקוראים לבונה.
שלב 3: מידע על בנאים משניים
בשלב הזה נסביר על בנאים משניים ונוסיף אחד לכיתה. בנוסף לקונסטרוקטור ראשי, שיכול לכלול בלוק אחד או יותר של init
, מחלקה ב-Kotlin יכולה לכלול גם קונסטרוקטור משני אחד או יותר כדי לאפשר העמסת קונסטרוקטורים, כלומר קונסטרוקטורים עם ארגומנטים שונים.
- במחלקת
Aquarium
, מוסיפים בנאי משני שמקבל מספר דגים כארגומנט, באמצעות מילת המפתחconstructor
. יוצרים מאפיין שלval
טנק לנפח המחושב של האקווריום בליטרים על סמך מספר הדגים. נניח שצריך 2 ליטר (2,000 סמ"ק) מים לכל דג, ועוד קצת מקום כדי שהמים לא יישפכו.
constructor(numberOfFish: Int) : this() {
// 2,000 cm^3 per fish + extra room so water doesn't spill
val tank = numberOfFish * 2000 * 1.1
}
- בתוך ה-constructor המשני, משאירים את האורך והרוחב (שהוגדרו ב-constructor הראשי) ללא שינוי, ומחשבים את הגובה שדרוש כדי שהנפח של המיכל יהיה הנפח הנתון.
// calculate the height needed
height = (tank / (length * width)).toInt()
- בפונקציה
buildAquarium()
, מוסיפים קריאה ליצירתAquarium
באמצעות בנאי המשנה החדש. מדפיסים את הגודל ואת הנפח.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
- מריצים את התוכנית ומסתכלים על הפלט.
⇒ aquarium initializing Volume: 80 l Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
שימו לב שהנפח מודפס פעמיים, פעם אחת על ידי הבלוק init
בבונה הראשי לפני שהבונה המשני מופעל, ופעם אחת על ידי הקוד ב-buildAquarium()
.
יכולתם לכלול את מילת המפתח constructor
גם בבונה הראשי, אבל ברוב המקרים זה לא נחוץ.
שלב 4: הוספה של פונקציית getter חדשה של מאפיין
בשלב הזה מוסיפים מאחזר מאפיינים מפורש. Kotlin מגדירה באופן אוטומטי getters ו-setters כשמגדירים מאפיינים, אבל לפעמים צריך לשנות או לחשב את הערך של מאפיין. לדוגמה, בדוגמה שלמעלה, הדפסתם את נפח הנתונים של Aquarium
. כדי להגדיר את עוצמת הקול כמאפיין, צריך להגדיר בשבילה משתנה ו-getter. מכיוון שצריך לחשב את volume
, פונקציית ה-getter צריכה להחזיר את הערך המחושב, ואפשר לעשות זאת באמצעות פונקציה של שורה אחת.
- במחלקה
Aquarium
, מגדירים מאפייןInt
בשםvolume
, ומגדירים שיטהget()
לחישוב הנפח בשורה הבאה.
val volume: Int
get() = width * height * length / 1000 // 1000 cm^3 = 1 l
- מסירים את הבלוק
init
שמדפיס את עוצמת הקול. - מסירים את הקוד ב-
buildAquarium()
שמדפיס את עוצמת הקול. - בשיטה
printSize()
, מוסיפים שורה להדפסת עוצמת הקול.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm "
)
// 1 l = 1000 cm^3
println("Volume: $volume l")
}
- מריצים את התוכנית ומסתכלים על הפלט.
⇒ aquarium initializing Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
המאפיינים והנפח זהים לאלה שהיו קודם, אבל הנפח מודפס רק פעם אחת אחרי שהאובייקט מאותחל באופן מלא על ידי בנאי ראשי ובנאי משני.
שלב 5: מוסיפים פונקציה להגדרת מאפיין
בשלב הזה יוצרים פונקציה חדשה להגדרת מאפיינים של עוצמת הקול.
- במחלקת
Aquarium
, משנים אתvolume
ל-var
כדי שאפשר יהיה להגדיר אותו יותר מפעם אחת. - מוסיפים פונקציית setter למאפיין
volume
על ידי הוספת שיטתset()
מתחת לפונקציית getter, שמחשבת מחדש את הגובה על סמך כמות המים שסופקה. לפי המוסכמה, שם הפרמטר של הפונקציה להגדרת ערך הואvalue
, אבל אפשר לשנות אותו אם רוצים.
var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- ב-
buildAquarium()
, מוסיפים קוד כדי להגדיר את נפח האקווריום ל-70 ליטר. מדפיסים את הגודל החדש.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
aquarium6.volume = 70
aquarium6.printSize()
}
- מריצים שוב את התוכנית ומתבוננים בגובה ובעוצמת הקול ששונו.
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm
Volume: 70 l
עד עכשיו לא היו בקוד משנים של נראות, כמו public
או private
. הסיבה לכך היא שכל מה שכתוב ב-Kotlin הוא ציבורי כברירת מחדל, כלומר אפשר לגשת לכל דבר בכל מקום, כולל מחלקות, שיטות, מאפיינים ומשתני חברים.
ב-Kotlin, למחלקות, לאובייקטים, לממשקים, לקונסטרוקטורים, לפונקציות, למאפיינים ולפונקציות setter שלהם יכולים להיות משני הרשאות גישה:
-
public
– גלוי מחוץ לכיתה. כברירת מחדל, כל דבר הוא ציבורי, כולל משתנים ושיטות של המחלקה. internal
, כלומר הוא יהיה גלוי רק בתוך המודול הזה. מודול הוא קבוצה של קובצי Kotlin שעברו קומפילציה יחד, למשל, ספרייה או אפליקציה.-
private
פירושו שהיא תהיה גלויה רק במחלקה הזו (או בקובץ המקור אם עובדים עם פונקציות). -
protected
זהה ל-protected
, אבל הוא יהיה גלוי גם לכל תת-המחלקות.private
מידע נוסף זמין במאמר Visibility Modifiers (משני הגדרות החשיפה) במסמכי התיעוד של Kotlin.
משתני חברים
מאפיינים בתוך מחלקה, או משתני חברים, הם public
כברירת מחדל. אם מגדירים אותם באמצעות var
, הם ניתנים לשינוי, כלומר אפשר לקרוא ולכתוב אותם. אם מגדירים אותם באמצעות val
, הם יהיו לקריאה בלבד אחרי האתחול.
אם רוצים שנכס יהיה כזה שהקוד יכול לקרוא או לכתוב בו, אבל קוד חיצוני יכול רק לקרוא בו, אפשר להשאיר את הנכס ואת שיטת ה-getter שלו כציבוריים ולהגדיר את שיטת ה-setter כפרטית, כמו בדוגמה שלמטה.
var volume: Int
get() = width * height * length / 1000
private set(value) {
height = (value * 1000) / (width * length)
}
במשימה הזו תלמדו איך פועלים מחלקות משנה והורשה ב-Kotlin. הם דומים למה שראיתם בשפות אחרות, אבל יש כמה הבדלים.
ב-Kotlin, כברירת מחדל, אי אפשר ליצור מחלקות משנה. באופן דומה, אי אפשר לבטל את ההגדרה של מאפיינים ומשתני חברים באמצעות מחלקות משנה (אבל אפשר לגשת אליהם).
כדי לאפשר יצירת מחלקת משנה, צריך לסמן את המחלקה כ-open
. באופן דומה, צריך לסמן מאפיינים ומשתני חברים כ-open
כדי לשנות אותם במחלקת המשנה. חובה להשתמש במילת המפתח open
כדי למנוע חשיפה לא מכוונת של פרטי ההטמעה כחלק מהממשק של המחלקה.
שלב 1: פותחים את הכיתה 'אקווריום'
בשלב הזה, יוצרים את Aquarium
המחלקה open
, כדי שאפשר יהיה לבטל אותה בשלב הבא.
- מסמנים את המחלקה
Aquarium
ואת כל המאפיינים שלה במילת המפתחopen
.
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
open var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- מוסיפים נכס פתוח
shape
עם הערך"rectangle"
.
open val shape = "rectangle"
- מוסיפים נכס פתוח
water
עם getter שמחזיר 90% מהנפח שלAquarium
.
open var water: Double = 0.0
get() = volume * 0.9
- מוסיפים קוד לשיטה
printSize()
כדי להדפיס את הצורה ואת כמות המים כאחוז מהנפח.
fun printSize() {
println(shape)
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
// 1 l = 1000 cm^3
println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
- ב-
buildAquarium()
, משנים את הקוד כדי ליצורAquarium
עםwidth = 25
,length = 25
ו-height = 40
.
fun buildAquarium() {
val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
aquarium6.printSize()
}
- מריצים את התוכנית ובודקים את הפלט החדש.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full)
שלב 2: יצירת מחלקת משנה
- יוצרים מחלקת משנה של
Aquarium
בשםTowerTank
, שמטמיעה מיכל גלילי מעוגל במקום מיכל מלבני. אפשר להוסיף אתTowerTank
מתחת ל-Aquarium
, כי אפשר להוסיף עוד כיתה באותו קובץ כמו הכיתהAquarium
. - ב-
TowerTank
, מחליפים את המאפייןheight
שמוגדר בבונה. כדי לבטל את ההגדרה של מאפיין, משתמשים במילת המפתחoverride
במחלקת המשנה.
- מגדירים את ה-constructor של
TowerTank
כך שיקבלdiameter
. משתמשים ב-diameter
גם ל-length
וגם ל-width
כשקוראים ל-constructor במחלקת העלAquarium
.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
- מבטלים את ברירת המחדל של מאפיין הנפח כדי לחשב גליל. הנוסחה לחישוב נפח של צילינדר היא פאי כפול הרדיוס בריבוע כפול הגובה. צריך לייבא את הקבוע
PI
מ-java.lang.Math
.
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
- ב-
TowerTank
, מחליפים את הנכסwater
ל-80% מהעוצמה.
override var water = volume * 0.8
- שינוי הערך של
shape
ל-"cylinder"
.
override val shape = "cylinder"
- המחלקת
TowerTank
הסופית שלכם צריכה להיראות כמו הקוד שבהמשך.
Aquarium.kt
:
package example.myapp
import java.lang.Math.PI
... // existing Aquarium class
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
override var water = volume * 0.8
override val shape = "cylinder"
}
- ב-
buildAquarium()
, תיצורTowerTank
בקוטר 25 ס"מ ובגובה 45 ס"מ. מדפיסים את הגודל.
main.kt:
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium(width = 25, length = 25, height = 40)
myAquarium.printSize()
val myTower = TowerTank(diameter = 25, height = 40)
myTower.printSize()
}
- מריצים את התוכנית ומסתכלים על הפלט.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full) aquarium initializing cylinder Width: 25 cm Length: 25 cm Height: 40 cm Volume: 18 l Water: 14.4 l (80.0% full)
לפעמים רוצים להגדיר התנהגות או מאפיינים משותפים שיהיו משותפים לכמה מחלקות קשורות. ב-Kotlin יש שתי דרכים לעשות את זה: ממשקים ומחלקות אבסטרקטיות. במשימה הזו יוצרים מחלקה מופשטת AquariumFish
למאפיינים שמשותפים לכל הדגים. יוצרים ממשק בשם FishAction
כדי להגדיר התנהגות משותפת לכל הדגים.
- אי אפשר ליצור מופע של מחלקה מופשטת או של ממשק בפני עצמו, כלומר אי אפשר ליצור אובייקטים מהסוגים האלה ישירות.
- למחלקות מופשטות יש בנאים.
- לממשקים לא יכולה להיות לוגיקה של בנאי או מצב מאוחסן.
שלב 1. יצירת מחלקה מופשטת
- בקטע example.myapp, יוצרים קובץ חדש,
AquariumFish.kt
. - יוצרים כיתה, שנקראת גם
AquariumFish
, ומסמנים אותה ב-abstract
. - מוסיפים מאפיין
String
אחד,color
, ומסמנים אותו באמצעותabstract
.
package example.myapp
abstract class AquariumFish {
abstract val color: String
}
- יוצרים שתי מחלקות משנה של
AquariumFish
, Shark
ו-Plecostomus
. - מכיוון ש-
color
הוא מופשט, מחלקות המשנה חייבות ליישם אותו. הופכים אתShark
לאפור ואתPlecostomus
לזהב.
class Shark: AquariumFish() {
override val color = "gray"
}
class Plecostomus: AquariumFish() {
override val color = "gold"
}
- ב-main.kt, יוצרים פונקציה
makeFish()
כדי לבדוק את הכיתות. יוצרים מופע שלShark
ושלPlecostomus
, ואז מדפיסים את הצבע של כל אחד מהם. - מוחקים את קוד הבדיקה הקודם ב-
main()
ומוסיפים קריאה ל-makeFish()
. הקוד שלכם צריך להיראות בערך כמו הקוד שבהמשך.
main.kt
:
package example.myapp
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
println("Plecostomus: ${pleco.color}")
}
fun main () {
makeFish()
}
- מריצים את התוכנית ומסתכלים על הפלט.
⇒ Shark: gray Plecostomus: gold
התרשים הבא מייצג את המחלקה Shark
ואת המחלקה Plecostomus
, שהן מחלקות משנה של המחלקה המופשטת AquariumFish
.
שלב 2. יצירת ממשק
- ב-AquariumFish.kt, יוצרים ממשק בשם
FishAction
עם method בשםeat()
.
interface FishAction {
fun eat()
}
- מוסיפים
FishAction
לכל אחת ממחלקות המשנה, ומטמיעים אתeat()
כך שהיא תדפיס את הפעולות של הדג.
class Shark: AquariumFish(), FishAction {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
class Plecostomus: AquariumFish(), FishAction {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- בפונקציה
makeFish()
, כל דג שיצרתם אוכל משהו באמצעות קריאה לפונקציהeat()
.
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
shark.eat()
println("Plecostomus: ${pleco.color}")
pleco.eat()
}
- מריצים את התוכנית ומסתכלים על הפלט.
⇒ Shark: gray hunt and eat fish Plecostomus: gold eat algae
בתרשים הבא מוצגות המחלקה Shark
והמחלקה Plecostomus
, ששתיהן מורכבות מהממשק FishAction
ומטמיעות אותו.
מתי כדאי להשתמש במחלקות מופשטות ומתי בממשקים
הדוגמאות שלמעלה הן פשוטות, אבל אם יש לכם הרבה מחלקות שקשורות זו לזו, מחלקות מופשטות וממשקים יכולים לעזור לכם לשמור על עיצוב נקי ומאורגן יותר, וגם להקל על התחזוקה.
כמו שצוין למעלה, למחלקות אבסטרקטיות יכולים להיות constructors, אבל לממשקים לא יכולים להיות, אבל חוץ מזה הם דומים מאוד. אז מתי כדאי להשתמש בכל אחת מהן?
כשמשתמשים בממשקים כדי ליצור מחלקה, הפונקציונליות של המחלקה מורחבת באמצעות מופעי המחלקה שהיא מכילה. הקומפוזיציה נוטה להקל על שימוש חוזר בקוד ועל ניתוח שלו בהשוואה להורשה ממחלקה מופשטת. בנוסף, אפשר להשתמש בכמה ממשקים במחלקה, אבל אפשר ליצור מחלקת משנה רק ממחלקה מופשטת אחת.
קומפוזיציה מובילה לרוב להצפנה טובה יותר, לצימוד (תלות הדדית) נמוך יותר, לממשקים נקיים יותר ולקוד שקל יותר להשתמש בו. לכן, השימוש בקומפוזיציה עם ממשקים הוא העיצוב המועדף. מצד שני, ירושה ממחלקה מופשטת מתאימה באופן טבעי לפתרון בעיות מסוימות. לכן, מומלץ להשתמש בהרכבה, אבל אם יש מקרים שבהם עדיף להשתמש בירושה, אפשר לעשות את זה גם ב-Kotlin.
- כדאי להשתמש בממשק אם יש הרבה שיטות והטמעה אחת או שתיים שמוגדרות כברירת מחדל, כמו בדוגמה
AquariumAction
שבהמשך.
interface AquariumAction {
fun eat()
fun jump()
fun clean()
fun catchFish()
fun swim() {
println("swim")
}
}
- משתמשים במחלקה מופשטת בכל פעם שלא ניתן להשלים מחלקה. לדוגמה, אם נחזור לכיתה
AquariumFish
, אפשר לגרום לכלAquariumFish
להטמיע אתFishAction
, ולספק הטמעה שמוגדרת כברירת מחדל עבורeat
, תוך השארתcolor
מופשטת, כי אין באמת צבע ברירת מחדל לדגים.
interface FishAction {
fun eat()
}
abstract class AquariumFish: FishAction {
abstract val color: String
override fun eat() = println("yum")
}
במשימה הקודמת הצגנו מחלקות מופשטות, ממשקים ואת הרעיון של קומפוזיציה. העברת סמכויות בממשק היא טכניקה מתקדמת שבה השיטות של הממשק מיושמות על ידי אובייקט עזר (או אובייקט מועבר), שמשמש לאחר מכן מחלקה. הטכניקה הזו יכולה להיות שימושית כשמשתמשים בממשק בסדרה של מחלקות לא קשורות: מוסיפים את הפונקציונליות הנדרשת של הממשק למחלקת עזר נפרדת, וכל אחת מהמחלקות משתמשת במופע של מחלקת העזר כדי להטמיע את הפונקציונליות.
במשימה הזו, אתם משתמשים בהעברת הרשאות לממשק כדי להוסיף פונקציונליות לכיתה.
שלב 1: יוצרים ממשק חדש
- ב-AquariumFish.kt, מסירים את המחלקה
AquariumFish
. במקום לרשת מהמחלקהAquariumFish
, המחלקותPlecostomus
ו-Shark
יטמיעו ממשקים גם לפעולת הדג וגם לצבע שלו. - יוצרים ממשק חדש,
FishColor
, שמגדיר את הצבע כמחרוזת.
interface FishColor {
val color: String
}
- משנים את
Plecostomus
כדי להטמיע שני ממשקים,FishAction
ו-FishColor
. צריך לשנות את הערך שלcolor
מ-FishColor
ואת הערך שלeat()
מ-FishAction
.
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- משנים את המחלקה
Shark
כך שהיא תטמיע גם את שני הממשקים,FishAction
ו-FishColor
, במקום לרשת מ-AquariumFish
.
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
- הקוד הסופי אמור להיראות כך:
package example.myapp
interface FishAction {
fun eat()
}
interface FishColor {
val color: String
}
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
שלב 2: יוצרים מחלקה יחידה
לאחר מכן מטמיעים את ההגדרה של חלק ההרשאה על ידי יצירת מחלקה מסייעת שמטמיעה את FishColor
. יוצרים מחלקה בסיסית בשם GoldColor
שמטמיעה את FishColor
– כל מה שהיא עושה זה להצהיר שהצבע שלה הוא זהב.
אין טעם ליצור כמה מופעים של GoldColor
, כי כולם יעשו בדיוק את אותו הדבר. לכן, ב-Kotlin אפשר להצהיר על מחלקה שניתן ליצור ממנה רק מופע אחד באמצעות מילת המפתח object
במקום class
. Kotlin תיצור את המופע הזה, והמופע הזה יקבל הפניה משם המחלקה. אחרי זה כל האובייקטים האחרים יכולים להשתמש רק במופע הזה – אין אפשרות ליצור מופעים אחרים של המחלקה הזו. אם אתם מכירים את תבנית העיצוב Singleton, כך מטמיעים Singleton ב-Kotlin.
- ב-AquariumFish.kt, יוצרים אובייקט בשם
GoldColor
. שינוי הצבע מברירת המחדל.
object GoldColor : FishColor {
override val color = "gold"
}
שלב 3: הוספת העברת הרשאות לממשק של FishColor
עכשיו אפשר להשתמש בהענקת גישה לממשק.
- ב-AquariumFish.kt, מסירים את ההחלפה של
color
מ-Plecostomus
. - משנים את המחלקה
Plecostomus
כדי לקבל את הצבע שלה מ-GoldColor
. כדי לעשות זאת, מוסיפיםby GoldColor
להצהרת המחלקה ויוצרים את ההעברה. המשמעות היא שבמקום להטמיע אתFishColor
, צריך להשתמש בהטמעה שסופקה על ידיGoldColor
. לכן, בכל פעם שמתבצעת גישה אלcolor
, הגישה מועברת אלGoldColor
.
class Plecostomus: FishAction, FishColor by GoldColor {
override fun eat() {
println("eat algae")
}
}
בכיתה כמו שהיא, כל הפלקו יהיו בצבע זהב, אבל הדגים האלה מגיעים למעשה בהרבה צבעים. כדי לפתור את הבעיה, אפשר להוסיף פרמטר של בנאי לצבע עם GoldColor
כצבע ברירת המחדל של Plecostomus
.
- משנים את המחלקה
Plecostomus
כך שתקבל אתfishColor
עם הבונה שלה, ומגדירים את ברירת המחדל שלה ל-GoldColor
. שינוי ההרשאה מ-by GoldColor
ל-by fishColor
.
class Plecostomus(fishColor: FishColor = GoldColor): FishAction,
FishColor by fishColor {
override fun eat() {
println("eat algae")
}
}
שלב 4: הוספת העברת הרשאות לממשק עבור FishAction
באותו אופן, אפשר להשתמש בהענקת הרשאה לממשק עבור FishAction
.
- ב-AquariumFish.kt יוצרים מחלקה
PrintingFishAction
שמטמיעה אתFishAction
, שמקבלתString
,food
, ואז מדפיסה את מה שהדג אוכל.
class PrintingFishAction(val food: String) : FishAction {
override fun eat() {
println(food)
}
}
- במחלקת
Plecostomus
, מסירים את פונקציית ההחלפהeat()
, כי תחליפו אותה בהענקת הרשאה. - בהצהרה של
Plecostomus
, מעבירים אתFishAction
אלPrintingFishAction
, ומעבירים את"eat algae"
. - בגלל כל ההענקות האלה של הרשאות, אין קוד בגוף של המחלקה
Plecostomus
, ולכן צריך להסיר את{}
, כי כל ההגדרות שמוגדרות מחדש מטופלות על ידי הענקת הרשאות לממשק
class Plecostomus (fishColor: FishColor = GoldColor):
FishAction by PrintingFishAction("eat algae"),
FishColor by fishColor
בתרשים הבא מוצגים המחלקות Shark
ו-Plecostomus
, ששתיהן מורכבות מהממשקים PrintingFishAction
ו-FishColor
, אבל מייצגות את ההטמעה שלהן.
העברת הרשאות לממשק היא תכונה שימושית, וכדאי לחשוב איך להשתמש בה בכל פעם שאתם עשויים להשתמש במחלקה מופשטת בשפה אחרת. היא מאפשרת להשתמש בהרכבה כדי להוסיף התנהגויות, במקום לדרוש הרבה מחלקות משנה, שכל אחת מהן מתמחה בדרך אחרת.
מחלקת נתונים דומה ל-struct
בשפות אחרות – היא קיימת בעיקר כדי להכיל נתונים מסוימים – אבל אובייקט של מחלקת נתונים הוא עדיין אובייקט. לאובייקטים של מחלקות נתונים ב-Kotlin יש כמה יתרונות נוספים, כמו כלי עזר להדפסה ולהעתקה. במשימה הזו תיצרו מחלקת נתונים פשוטה ותלמדו על התמיכה ש-Kotlin מספקת למחלקות נתונים.
שלב 1: יוצרים סיווג נתונים
- מוסיפים חבילה חדשה
decor
מתחת לחבילה example.myapp כדי להכיל את הקוד החדש. לוחצים לחיצה ימנית על example.myapp בחלונית Project ובוחרים באפשרות File > New > Package (קובץ > חדש > חבילה). - בחבילה, יוצרים מחלקה חדשה בשם
Decoration
.
package example.myapp.decor
class Decoration {
}
- כדי להפוך את
Decoration
למחלקת נתונים, מוסיפים את מילת המפתחdata
לפני הצהרת המחלקה. - מוסיפים מאפיין
String
בשםrocks
כדי לספק לכיתה נתונים.
data class Decoration(val rocks: String) {
}
- בקובץ, מחוץ לכיתה, מוסיפים פונקציה
makeDecorations()
כדי ליצור ולהדפיס מופע שלDecoration
עם"granite"
.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
}
- מוסיפים פונקציה
main()
כדי לקרוא ל-makeDecorations()
, ומריצים את התוכנית. שימו לב לפלט הסביר שנוצר כי מדובר במחלקת נתונים.
⇒ Decoration(rocks=granite)
- ב-
makeDecorations()
, יוצרים שני אובייקטים נוספים שלDecoration
ששניהם בצבע 'אפור צפחה' ומדפיסים אותם.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
val decoration2 = Decoration("slate")
println(decoration2)
val decoration3 = Decoration("slate")
println(decoration3)
}
- ב-
makeDecorations()
, מוסיפים הצהרת הדפסה שמדפיסה את התוצאה של השוואה ביןdecoration1
לביןdecoration2
, ועוד הצהרה שמשווה ביןdecoration3
לביןdecoration2
. משתמשים בשיטה equals() שמופיעה במחלקות נתונים.
println (decoration1.equals(decoration2))
println (decoration3.equals(decoration2))
- מריצים את הקוד.
⇒ Decoration(rocks=granite) Decoration(rocks=slate) Decoration(rocks=slate) false true
שלב 2. שימוש בפירוק מבנה
כדי לגשת למאפיינים של אובייקט נתונים ולהקצות אותם למשתנים, אפשר להקצות אותם אחד בכל פעם, כמו בדוגמה הבאה.
val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver
במקום זאת, אפשר ליצור משתנים, אחד לכל נכס, ולהקצות את אובייקט הנתונים לקבוצת המשתנים. Kotlin מכניסה את ערך המאפיין לכל משתנה.
val (rock, wood, diver) = decoration
הפעולה הזו נקראת פירוק מבנה והיא קיצור דרך שימושי. מספר המשתנים צריך להיות זהה למספר המאפיינים, והמשתנים מוקצים בסדר שבו הם מוצהרים במחלקה. הנה דוגמה מלאה שאפשר לנסות בקובץ Decoration.kt.
// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}
fun makeDecorations() {
val d5 = Decoration2("crystal", "wood", "diver")
println(d5)
// Assign all properties to variables.
val (rock, wood, diver) = d5
println(rock)
println(wood)
println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver) crystal wood diver
אם לא צריך אחד או יותר מהמאפיינים, אפשר לדלג עליהם באמצעות _
במקום שם משתנה, כמו שמוצג בקוד שלמטה.
val (rock, _, diver) = d5
במשימה הזו תלמדו על כמה מחלקות מיוחדות ב-Kotlin, כולל:
- מחלקות Singleton
- טיפוסים בני מנייה (enum)
- מחלקות אטומות
שלב 1: נזכרים בכיתות סינגלטון
נזכרים בדוגמה הקודמת עם המחלקה GoldColor
.
object GoldColor : FishColor {
override val color = "gold"
}
מכיוון שכל מופע של GoldColor
עושה את אותו הדבר, הוא מוצהר כ-object
במקום כ-class
כדי להפוך אותו לסינגלטון. יכול להיות רק מופע אחד שלו.
שלב 2: יצירת enum
Kotlin תומכת גם ב-enums, שמאפשרים לכם למנות משהו ולהתייחס אליו לפי שם, בדומה לשפות אחרות. מצהירים על enum על ידי הוספת מילת המפתח enum
לפני ההצהרה. הצהרה בסיסית של enum צריכה לכלול רק רשימה של שמות, אבל אפשר גם להגדיר שדה אחד או יותר שמשויכים לכל שם.
- ב-Decoration.kt, אפשר לנסות דוגמה של enum.
enum class Color(val rgb: Int) {
RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}
סוגי enum דומים קצת לסינגלטונים – יכול להיות רק אחד, ורק אחד מכל ערך בספירה. לדוגמה, יכול להיות רק רכיב Color.RED
אחד, רכיב Color.GREEN
אחד ורכיב Color.BLUE
אחד. בדוגמה הזו, ערכי ה-RGB מוקצים למאפיין rgb
כדי לייצג את רכיבי הצבע. אפשר גם לקבל את הערך הסידורי של enum באמצעות המאפיין ordinal
ואת השם שלו באמצעות המאפיין name
.
- ננסה דוגמה נוספת של enum.
enum class Direction(val degrees: Int) {
NORTH(0), SOUTH(180), EAST(90), WEST(270)
}
fun main() {
println(Direction.EAST.name)
println(Direction.EAST.ordinal)
println(Direction.EAST.degrees)
}
⇒ EAST 2 90
שלב 3: יוצרים מחלקה אטומה
מחלקה חתומה היא מחלקה שאפשר ליצור ממנה מחלקת משנה, אבל רק בתוך הקובץ שבו היא מוצהרת. אם מנסים ליצור מחלקת משנה של המחלקה בקובץ אחר, מוצגת שגיאה.
מכיוון שהמחלקות ומחלקות המשנה נמצאות באותו קובץ, Kotlin תדע את כל מחלקות המשנה באופן סטטי. כלומר, בזמן ההידור, המהדר רואה את כל המחלקות ומחלקות המשנה ויודע שזה הכל, ולכן הוא יכול לבצע בדיקות נוספות בשבילכם.
- ב-AquariumFish.kt, אפשר לנסות דוגמה של מחלקה אטומה, בהתאם לנושא המים.
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()
fun matchSeal(seal: Seal): String {
return when(seal) {
is Walrus -> "walrus"
is SeaLion -> "sea lion"
}
}
אי אפשר ליצור מחלקת משנה (subclass) של המחלקה Seal
בקובץ אחר. אם רוצים להוסיף עוד סוגים של Seal
, צריך להוסיף אותם באותו קובץ. כך מחלקות חתומות הן דרך בטוחה לייצג מספר קבוע של סוגים. לדוגמה, מחלקות אטומות מצוינות להחזרת הצלחה או שגיאה מ-API של רשת.
השיעור הזה כלל הרבה נושאים. חלק גדול מהתכונות של Kotlin מוכרות משפות תכנות אחרות מונחות-עצמים, אבל יש בה גם תכונות נוספות שמאפשרות לכתוב קוד תמציתי וקריא.
מחלקות ובוני אובייקטים
- מגדירים מחלקה ב-Kotlin באמצעות
class
. - Kotlin יוצרת באופן אוטומטי פונקציות setter ו-getter למאפיינים.
- מגדירים את ה-constructor הראשי ישירות בהגדרת המחלקה. לדוגמה:
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
- אם קונסטרוקטור ראשי צריך קוד נוסף, כותבים אותו באחד או יותר מ
init
בלוקים. - אפשר להגדיר מחלקה עם בנאי משני אחד או יותר באמצעות
constructor
, אבל בסגנון Kotlin מקובל יותר להשתמש בפונקציית factory.
משנים של חשיפה ותת-מחלקות
- כל המחלקות והפונקציות ב-Kotlin הן
public
כברירת מחדל, אבל אפשר להשתמש במגדירי גישה כדי לשנות את הגישה ל-internal
,private
אוprotected
. - כדי ליצור מחלקת משנה, צריך לסמן את מחלקת האב בתג
open
. - כדי לשנות את ההתנהגות של methods ומאפיינים במחלקת משנה, צריך לסמן את ה-methods והמאפיינים ב-
open
במחלקת האב. - אפשר ליצור מחלקה משנית ממחלקה חתומה רק באותו קובץ שבו היא מוגדרת. כדי ליצור מחלקה חתומה, מוסיפים את הקידומת
sealed
להצהרה.
מחלקות נתונים, סינגלטונים וסוגי ספירה
- כדי ליצור סיווג נתונים, מוסיפים את הקידומת
data
להצהרה. - Destructuring היא דרך מקוצרת להקצאת המאפיינים של אובייקט
data
למשתנים נפרדים. - כדי ליצור מחלקת סינגלטון, משתמשים ב-
object
במקום ב-class
. - הגדרת enum באמצעות
enum class
.
מחלקות מופשטות, ממשקים והעברה
- מחלקות מופשטות וממשקים הם שתי דרכים לשתף התנהגות משותפת בין מחלקות.
- מחלקה אבסטרקטית מגדירה מאפיינים והתנהגות, אבל משאירה את ההטמעה למחלקות משנה.
- ממשק מגדיר התנהגות, ויכול לספק הטמעות שמוגדרות כברירת מחדל לחלק מההתנהגות או לכולה.
- כשמשתמשים בממשקים כדי ליצור מחלקה, הפונקציונליות של המחלקה מורחבת באמצעות מופעי המחלקה שהיא מכילה.
- העברת הרשאות בממשק משתמש מתבססת על קומפוזיציה, אבל גם מעבירה את ההטמעה למחלקות הממשק.
- קומפוזיציה היא דרך יעילה להוסיף פונקציונליות למחלקה באמצעות העברת הרשאות לממשק. בדרך כלל עדיף להשתמש בהרכבה, אבל יש בעיות שמתאים יותר לפתור באמצעות ירושה ממחלקה מופשטת.
תיעוד של Kotlin
אם אתם רוצים לקבל מידע נוסף על נושא כלשהו בקורס הזה, או אם נתקעתם, https://kotlinlang.org הוא המקום הכי טוב להתחיל בו.
- מחלקות וירושה
- Constructors
- פונקציות Factory
- מאפיינים ושדות
- משני חשיפה
- מחלקות אבסטרקטיות
- ממשקים
- הענקת הרשאות
- סיווג נתונים
- Equality
- פירוק מבנה
- הצהרות על אובייקטים
- מחלקות enum
- Sealed classes
- טיפול בשגיאות אופציונליות באמצעות מחלקות אטומות של Kotlin
מדריכים ל-Kotlin
באתר https://try.kotlinlang.org יש הדרכות מפורטות שנקראות Kotlin Koans, מפרש מבוסס-אינטרנט וסט מלא של מסמכי עזר עם דוגמאות.
קורס של Udacity
כדי לצפות בקורס של Udacity בנושא הזה, אפשר לעבור אל Kotlin Bootcamp for Programmers.
IntelliJ IDEA
מסמכי התיעוד של IntelliJ IDEA זמינים באתר JetBrains.
בקטע הזה מפורטות אפשרויות למשימות ביתיות לתלמידים שעובדים על ה-Codelab הזה כחלק מקורס בהנחיית מדריך. המורה צריך:
- אם צריך, מקצים שיעורי בית.
- להסביר לתלמידים איך להגיש מטלות.
- בודקים את שיעורי הבית.
אנשי ההוראה יכולים להשתמש בהצעות האלה כמה שרוצים, ומומלץ להם להקצות כל שיעורי בית אחרים שהם חושבים שמתאימים.
אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.
עונים על השאלות הבאות
שאלה 1
לכיתות יש שיטה מיוחדת שמשמשת כתוכנית ליצירת אובייקטים של אותה כיתה. מה שם השיטה?
▢ בנאי
▢ יוצר מופעים
▢ יצרן
▢ שרטוט
שאלה 2
איזו מההצהרות הבאות לגבי ממשקים ומחלקות אבסטרקטיות היא לא נכונה?
▢ מחלקות מופשטות יכולות לכלול בנאים.
▢ לממשקים לא יכולים להיות בנאים.
▢ אפשר ליצור מופעים של ממשקים ומחלקות מופשטות באופן ישיר.
▢ מחלקות משנה של המחלקה המופשטת חייבות להטמיע מאפיינים מופשטים.
שאלה 3
איזה מהבאים הוא לא משנה נראות ב-Kotlin למאפיינים, לשיטות וכו'?
▢ internal
▢ nosubclass
▢ protected
▢ private
שאלה 4
נתונה מחלקת הנתונים הבאה:data class Fish(val name: String, val species:String, val colors:String)
איזה מהקודים הבאים לא תקף ליצירה ולפירוק של אובייקט Fish
?
▢ val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")
▢ val (name2, _, colors2) = Fish("Bitey", "shark", "gray")
▢ val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")
▢ val (name4, species4, colors4) = Fish("Harry", "halibut")
שאלה 5
נניח שאתם הבעלים של גן חיות עם הרבה בעלי חיים שצריך לטפל בהם. מה מהפעולות הבאות לא נכלל בהטמעה של טיפול ותחזוקה?
▢ interface
לסוגים שונים של מזונות שבעלי חיים אוכלים.
▢ abstract Caretaker
כיתה שבה אפשר ליצור סוגים שונים של מטפלים.
▢ interface
על מתן מים נקיים לבעל חיים.
▢ כיתה data
לרשומה בלוח הזמנים של האכלה.
עוברים לשיעור הבא:
סקירה כללית של הקורס, כולל קישורים ל-Codelabs אחרים, זמינה במאמר "Kotlin Bootcamp for Programmers: Welcome to the course".