Kotlin Bootcamp for Programmers 2: Kotlin basics

ה-codelab הזה הוא חלק מקורס Kotlin Bootcamp for Programmers. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. אם יש לכם ידע בנושא, יכול להיות שתוכלו לדלג על חלק מהקטעים. הקורס הזה מיועד למתכנתים שמכירים שפה מונחית-אובייקטים ורוצים ללמוד Kotlin.

מבוא

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

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

מה שכדאי לדעת

  • איך יוצרים פרויקט ב-IntelliJ IDEA
  • איך פותחים ומריצים קוד ב-REPL (Read-Eval-Print Loop) של Kotlin ב-IntelliJ IDEA (Tools > Kotlin > Kotlin REPL)

מה תלמדו

  • איך משתמשים בסוגי נתונים, באופרטורים ובמשתנים של Kotlin
  • איך עובדים עם ערכים בוליאניים ותנאים
  • ההבדל בין משתנים שניתן להקצות להם ערך null לבין משתנים שלא ניתן להקצות להם ערך null
  • הסבר על מערכים, רשימות ולולאות ב-Kotlin

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

  • עבודה עם Kotlin REPL כדי ללמוד את היסודות של Kotlin

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

שלב 1: בודקים אופרטורים מספריים

  1. פותחים את IntelliJ IDEA, אם הוא עדיין לא פתוח.
  2. כדי לפתוח את Kotlin REPL, בוחרים באפשרות Tools (כלים) > Kotlin > Kotlin REPL.

ב-Kotlin, כמו בשפות אחרות, משתמשים בסימנים +, -, * ו-/ לחיבור, חיסור, כפל וחילוק. ‫Kotlin תומכת גם בסוגים שונים של מספרים, כמו Int,‏ Long,‏ Double ו-Float.

  1. מזינים את הביטויים הבאים ב-REPL. כדי לראות את התוצאה, מקישים על Control+Enter (Command+Enter ב-Mac) אחרי כל אחת מהפעולות.
1+1
⇒ res8: kotlin.Int = 2

53-3
⇒ res9: kotlin.Int = 50

50/10
⇒ res10: kotlin.Int = 5

1.0/2.0
⇒ res11: kotlin.Double = 0.5

2.0*3.5
⇒ res12: kotlin.Double = 7.0

שימו לב: התוצאות של פעולות שומרות על הסוגים של האופרנדים, כך ש-1/2 = 0, אבל 1.0/2.0 = 0.5.

  1. נסו להשתמש בביטויים עם שילובים שונים של מספרים שלמים ומספרים עשרוניים.
6*50
⇒ res13: kotlin.Int = 300

6.0*50.0
⇒ res14: kotlin.Double = 300.0

6.0*50
⇒ res15: kotlin.Double = 300.0
  1. להפעיל כמה methods על מספרים. ב-Kotlin, מספרים נשמרים כפרימיטיבים, אבל אפשר להפעיל עליהם מתודות כאילו הם אובייקטים.
2.times(3)
⇒ res5: kotlin.Int = 6

3.5.plus(4)
⇒ res8: kotlin.Double = 7.5

2.4.div(2)
⇒ res9: kotlin.Double = 1.2

שלב 2: תרגול השימוש בסוגים

ב-Kotlin אין המרה מרומזת בין סוגי מספרים, ולכן אי אפשר להקצות ערך short ישירות למשתנה long, או ערך Byte למשתנה Int. הסיבה לכך היא שהמרה משתמעת של מספרים היא מקור שכיח לשגיאות בתוכניות. תמיד אפשר להקצות ערכים מסוגים שונים באמצעות המרה.

  1. כדי לראות כמה מההמרות האפשריות, מגדירים משתנה מסוג Int ב-REPL.
val i: Int = 6
  1. יוצרים משתנה חדש, ואז מזינים את שם המשתנה שמופיע למעלה, ואחריו .to.
val b1 = i.to

ב-IntelliJ IDEA מוצגת רשימה של השלמות אפשריות. ההשלמה האוטומטית הזו פועלת עבור משתנים ואובייקטים מכל סוג.

  1. בוחרים באפשרות toByte() מהרשימה ומדפיסים את המשתנה.
val b1 = i.toByte()
println(b1)
⇒ 6
  1. הקצאת ערך Byte למשתנים מסוגים שונים.
val b2: Byte = 1 // OK, literals are checked statically
println(b2)
⇒ 1

val i1: Int = b2
⇒ error: type mismatch: inferred type is Byte but Int was expected

val i2: String = b2
⇒ error: type mismatch: inferred type is Byte but String was expected

val i3: Double = b2
⇒ error: type mismatch: inferred type is Byte but Double was expected
  1. אם משימות מסוימות החזירו שגיאות, נסו להפעיל Cast במקומן.
val i4: Int = b2.toInt() // OK!
println(i4)
⇒ 1

val i5: String = b2.toString()
println(i5)
⇒ 1

val i6: Double = b2.toDouble()
println(i6)
⇒ 1.0
  1. כדי להקל על הקריאה של קבועים מספריים ארוכים, אפשר להשתמש בקו תחתון במספרים ב-Kotlin, במקומות שבהם זה הגיוני. כדאי לנסות להזין קבועים מספריים שונים.
val oneMillion = 1_000_000
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

שלב 3: מידע על הערך של סוגי משתנים

‫Kotlin תומכת בשני סוגים של משתנים: משתנים שאפשר לשנות ומשתנים שלא ניתן לשנות. בעזרת val, אפשר להקצות ערך פעם אחת. אם תנסו להקצות משהו שוב, תקבלו שגיאה. באמצעות var, אפשר להקצות ערך ואז לשנות אותו בהמשך התוכנית.

  1. מגדירים משתנים באמצעות val ו-var ואז מקצים להם ערכים חדשים.
var fish = 1
fish = 2
val aquarium = 1
aquarium = 2
⇒ error: val cannot be reassigned

אפשר להקצות ל-fish ערך, ואז להקצות לו ערך חדש, כי הוא מוגדר באמצעות var. ניסיון להקצות ערך חדש ל-aquarium יוביל לשגיאה כי הוא מוגדר באמצעות val.

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

  1. מגדירים כמה משתנים ומציינים את הסוג שלהם באופן מפורש.
var fish: Int = 12
var lakes: Double = 2.5

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

שלב 4: מידע על מחרוזות

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

  1. יוצרים תבנית מחרוזת.
val numberOfFish = 5
val numberOfPlants = 12
"I have $numberOfFish fish" + " and $numberOfPlants plants"
⇒ res20: kotlin.String = I have 5 fish and 12 plants
  1. יוצרים תבנית מחרוזת עם ביטוי בתוכה. כמו בשפות אחרות, הערך יכול להיות תוצאה של ביטוי. משתמשים בסוגריים מסולסלים {} כדי להגדיר את הביטוי.
"I have ${numberOfFish + numberOfPlants} fish and plants"
⇒ res21: kotlin.String = I have 17 fish and plants

במשימה הזו תלמדו על ערכים בוליאניים ועל בדיקת תנאים בשפת התכנות Kotlin. בדומה לשפות אחרות, ב-Kotlin יש ערכים בוליאניים ואופרטורים בוליאניים כמו קטן מ-, שווה ל-, גדול מ- וכן הלאה (<,‏ ==,‏ >,‏ !=,‏ <=,‏ >=).

  1. כותבים דף חשבון ב-if או ב-else.
val numberOfFish = 50
val numberOfPlants = 23
if (numberOfFish > numberOfPlants) {
    println("Good ratio!") 
} else {
    println("Unhealthy ratio")
}
⇒ Good ratio!
  1. אפשר לנסות להשתמש בטווח בהצהרת if. ב-Kotlin, התנאי שאתם בודקים יכול להשתמש גם בטווחים.
val fish = 50
if (fish in 1..100) {
    println(fish)
}
⇒ 50
  1. כתיבת if עם כמה מקרים. לתנאים מורכבים יותר, משתמשים ב-&&logical and|| וב-&&logical or||. כמו בשפות אחרות, אפשר להשתמש ב-else if כדי להגדיר כמה מקרים.
if (numberOfFish == 0) {
    println("Empty tank")
} else if (numberOfFish < 40) {
    println("Got fish!")
} else {
    println("That's a lot of fish!")
}
⇒ That's a lot of fish!
  1. כדאי לנסות דף חשבון ב-when. יש דרך טובה יותר לכתוב את סדרת ההצהרות if/else if/else ב-Kotlin, באמצעות ההצהרה when, שדומה להצהרה switch בשפות אחרות. אפשר להשתמש גם בטווחים בתנאים בהצהרת when.
when (numberOfFish) {
    0  -> println("Empty tank")
    in 1..39 -> println("Got fish!")
    else -> println("That's a lot of fish!")
}
⇒ That's a lot of fish!

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

שלב 1: מידע על אפשרות ההגדרה כ-null

כברירת מחדל, אי אפשר null משתנים.

  1. מגדירים Int ומקצים לו null.
var rocks: Int = null
⇒ error: null can not be a value of a non-null type Int
  1. משתמשים באופרטור סימן השאלה, ?, אחרי הסוג כדי לציין שמשתנה יכול להיות null. מגדירים Int? ומקצים לו null.
var marbles: Int? = null

אם יש לכם סוגי נתונים מורכבים, כמו רשימה:

  • אפשר לאפשר שהאלמנטים ברשימה יהיו null.
  • אפשר לאפשר שהרשימה תהיה null, אבל אם היא לא null, אי אפשר לאפשר שהרכיבים שלה יהיו null.
  • אפשר לאפשר שהרשימה או הרכיבים יהיו null.

רשימות וכמה סוגים מורכבים אחרים של נתונים מוסברים במשימה מאוחרת יותר.

שלב 2: מידע על ? האופרטורים and ו-?:

אתם יכולים לבדוק אם יש null באמצעות האופרטור ?, וכך לא תצטרכו לכתוב הרבה הצהרות if/else.

  1. כותבים קוד בדרך הארוכה יותר כדי לבדוק אם המשתנה fishFoodTreats הוא לא null. לאחר מכן מקטינים את המשתנה.
var fishFoodTreats = 6
if (fishFoodTreats != null) {
    fishFoodTreats = fishFoodTreats.dec()
}
  1. עכשיו נראה איך כותבים את זה ב-Kotlin באמצעות האופרטור ?.
var fishFoodTreats = 6
fishFoodTreats = fishFoodTreats?.dec()
  1. אפשר גם לשרשר בדיקות null באמצעות האופרטור ?:. דוגמה:
fishFoodTreats = fishFoodTreats?.dec() ?: 0

זוהי קיצור דרך לביטוי 'אם fishFoodTreats לא שווה ל-null, מפחיתים 1 מהערך ומשתמשים בו; אחרת משתמשים בערך אחרי ?:, שהוא 0'. אם הערך של fishFoodTreats הוא null, ההערכה נעצרת והשיטה dec() לא נקראת.

הערה לגבי מצביעים ריקים

אם אתם ממש אוהבים את NullPointerExceptions, אתם יכולים להשתמש בהם ב-Kotlin. אופרטור הטענה not-null,‏ !! (סימן קריאה כפול), ממיר כל ערך לסוג שהוא לא null, וזורק חריגה אם הערך הוא null.

val len = s!!.length   // throws NullPointerException if s is null

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

שלב 1: יוצרים רשימות

רשימות הן סוג בסיסי ב-Kotlin, והן דומות לרשימות בשפות אחרות.

  1. מגדירים רשימה באמצעות listOf ומדפיסים אותה. אי אפשר לשנות את הרשימה הזו.
val school = listOf("mackerel", "trout", "halibut")
println(school)
⇒ [mackerel, trout, halibut]
  1. הצהרה על רשימה שאפשר לשנות באמצעות mutableListOf. מסירים פריט.
val myList = mutableListOf("tuna", "salmon", "shark")
myList.remove("shark")
⇒ res36: kotlin.Boolean = true

השיטה remove() מחזירה true אם היא מסירה בהצלחה את הפריט שהועבר.

שלב 2: יצירת מערכים

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

הכללים לגבי השימוש ב-val וב-var זהים במערכים וברשימות.

  1. מצהירים על מערך של מחרוזות באמצעות arrayOf. משתמשים בכלי המערך java.util.Arrays.toString() כדי להדפיס אותו.
val school = arrayOf("shark", "salmon", "minnow")
println(java.util.Arrays.toString(school))
⇒ [shark, salmon, minnow]
  1. למערך שהוגדר באמצעות arrayOf אין סוג שמשויך לאלמנטים, כך שאפשר לשלב סוגים, וזה שימושי. הצהרה על מערך עם סוגים שונים.
val mix = arrayOf("fish", 2)
  1. אפשר גם להגדיר מערכים עם סוג אחד לכל הרכיבים. מגדירים מערך של מספרים שלמים באמצעות intArrayOf(). יש בנאים מקבילים, או פונקציות ליצירת מופעים, למערכים מסוגים אחרים.
val numbers = intArrayOf(1,2,3)
  1. משלבים בין שתי מערכים באמצעות האופרטור +.
val numbers = intArrayOf(1,2,3)
val numbers3 = intArrayOf(4,5,6)
val foo2 = numbers3 + numbers
println(foo2[5])
=> 3
  1. נסו שילובים שונים של מערכים ורשימות מקוננים. כמו בשפות אחרות, אפשר להשתמש במערכים וברשימות מקוננים. כלומר, כשמציבים מערך בתוך מערך, מקבלים מערך של מערכים – ולא מערך שטוח של התוכן של שני המערכים. הרכיבים של מערך יכולים להיות גם רשימות, והרכיבים של רשימות יכולים להיות מערכים.
val numbers = intArrayOf(1, 2, 3)
val oceans = listOf("Atlantic", "Pacific")
val oddList = listOf(numbers, oceans, "salmon")
println(oddList)
⇒ [[I@89178b4, [Atlantic, Pacific], salmon]

הרכיב הראשון, numbers, הוא Array. כשלא משתמשים בכלי המערך כדי להדפיס אותו, Kotlin מדפיסה את הכתובת במקום את התוכן של המערך.

  1. אחת התכונות הנחמדות של Kotlin היא שאפשר לאתחל מערכים באמצעות קוד במקום לאתחל אותם ל-0. דוגמה:
val array = Array (5) { it * 2 }
println(java.util.Arrays.toString(array))
⇒ [0, 2, 4, 6, 8]

קוד האתחול נמצא בין הסוגריים המסולסלים, {}. בקוד, it מתייחס לאינדקס המערך, שמתחיל ב-0.

שלב 3: יצירת לולאות

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

  1. יוצרים מערך. משתמשים בלולאת for כדי לעבור על המערך ולהדפיס את האלמנטים.
val school = arrayOf("shark", "salmon", "minnow")
for (element in school) {
    print(element + " ")
}
⇒ shark salmon minnow
  1. ב-Kotlin, אפשר להשתמש בלולאה כדי לעבור על האלמנטים והאינדקסים בו-זמנית. דוגמה:
for ((index, element) in school.withIndex()) {
    println("Item at $index is $element\n")
}
⇒ Item at 0 is shark
Item at 1 is salmon
Item at 2 is minnow
  1. כדאי לנסות גדלים וטווחים שונים של צעדים. אפשר לציין טווחים של מספרים או של תווים, בסדר אלפביתי. כמו בשפות אחרות, לא חייבים להתקדם בצעד אחד. אפשר להשתמש במקש downTo כדי לחזור אחורה.
for (i in 1..5) print(i)
⇒ 12345

for (i in 5 downTo 1) print(i)
⇒ 54321

for (i in 3..6 step 2) print(i)
⇒ 35

for (i in 'd'..'g') print (i)
⇒ defg
  1. כדאי לנסות כמה לולאות. בדומה לשפות אחרות, ב-Kotlin יש לולאות while, לולאות do...while ואופרטורים ++ ו---. ב-Kotlin יש גם לולאות repeat.
var bubbles = 0
while (bubbles < 50) {
    bubbles++
}
println("$bubbles bubbles in the water\n")

do {
    bubbles--
} while (bubbles > 50)
println("$bubbles bubbles in the water\n")

repeat(2) {
     println("A fish is swimming")
}
⇒ 50 bubbles in the water
49 bubbles in the water
A fish is swimmingA fish is swimming

‫Kotlin דומה מאוד לשפות אחרות כשמדובר ביסודות כמו אופרטורים, רשימות ולולאות, אבל יש כמה הבדלים חשובים.

התכונות הבאות עשויות להיות שונות ב-Kotlin ממה שאתם רגילים בשפות אחרות:

  • אי אפשר להמיר סוגי Kotlin באופן מרומז – צריך להשתמש בהמרת טיפוסים.
  • אפשר להקצות משתנים שהוגדרו באמצעות val רק פעם אחת.
  • כברירת מחדל, משתני Kotlin לא יכולים להיות null. משתמשים ב-? כדי להגדיר משתנים כ-nullable.
  • ב-Kotlin, אפשר להשתמש בלולאת for כדי לעבור על האינדקסים והרכיבים של מערך בו-זמנית.

הקונסטרוקציות הבאות בשפת התכנות Kotlin דומות לאלה שבשפות אחרות:

  • מערכים ורשימות יכולים להכיל סוג יחיד או סוגים מעורבים.
  • אפשר להטמיע מערכים ורשימות בתוך מערכים ורשימות אחרים.
  • אפשר ליצור לולאות עם for,‏ while,‏ do/while ו-repeat.
  • ההצהרה when היא הגרסה של Kotlin להצהרה switch, אבל when גמישה יותר.

תיעוד של Kotlin

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

מדריכים ל-Kotlin

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

קורס של Udacity

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

IntelliJ IDEA

מסמכי התיעוד של IntelliJ IDEA זמינים באתר JetBrains.

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

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

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

אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.

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

שאלה 1

איזו מהאפשרויות הבאות מגדירה רשימה של מחרוזות שלא ניתן לשנות?

val school = arrayOf("shark", "salmon", "minnow")

var school = arrayOf("shark", "salmon", "minnow")

val school = listOf("shark", "salmon", "minnow")

val school = mutableListOf("shark", "salmon", "minnow")

שאלה 2

מה יהיה הפלט של הקוד הבא?
for (i in 3..8 step 2) print(i)

▢ 345678

▢ 468

▢ 38

▢ 357

שאלה 3

מה המטרה של סימן השאלה בקוד הזה?
var rocks: Int? = 3

‫▢ סוג המשתנה rocks לא קבוע.

‫▢ אפשר להגדיר את המשתנה rocks כ-null.

‫▢ אי אפשר להגדיר את המשתנה rocks כ-null.

‫▢ המשתנה rocks לא צריך להיות מאותחל באופן מיידי.

עוברים לשיעור הבא: 3. פונקציות

סקירה כללית של הקורס, כולל קישורים ל-Codelabs אחרים, זמינה במאמר "Kotlin Bootcamp for Programmers: Welcome to the course".