מחנה קוטלין למתכננים 2: היסודות של קוטלין

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

מבוא

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

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

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

  • איך יוצרים פרויקט ב- IntelliJ IDEA
  • איך לפתוח ולהפעיל קוד ב-Kotlin's REPL (Read-Eval-Print Loop) ב- IntelliJ IDEA (כלים > Kotlin > Kotlin REPL)

מה תלמדו

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

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

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

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

שלב 1: עיון באופרטורים המספריים

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

כמו בשפות אחרות, קוטלין משתמשת ב-+, ב--, ב-* וב-/ פלוס, מינוס, שעות וחלוקה. 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. התקשרות למספר שיטות במספרים. קוטלין שומר על מספרים כקווים בסיסיים, אבל הוא מאפשר לקרוא שיטות למספרים כאילו הם אובייקטים.
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 לא ממירה באופן מפורש בין סוגי מספרים, ולכן אי אפשר להקצות ערך קצר ישירות למשתנה ארוך או Byte לערך Int. הסיבה לכך היא שהמרה מסוג מספר מרומז היא מקור נפוץ לשגיאות בתוכניות. תמיד אפשר להקצות ערכים מסוגים שונים על ידי העברה (cast).

  1. כדי לראות חלק מההעברה (cast) האפשרית, צריך להגדיר משתנה מסוג 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. כדאי להפעיל את ההעברה למטלות שהחזירו שגיאות.
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. כדי להקל על הקריאה של קבועים מספריים ארוכים, קוטלין מאפשרת למקם קווים תחתונים במספרים, לפי הצורך. כדאי לנסות להזין קבועים מספריים שונים.
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 עם מספר מקרים. לתנאים מורכבים יותר, צריך להשתמש בלוגיקה וב-&& ובלוגיות או ||. כמו בשפות אחרות, יש לך אפשרות להשתמש ב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 בקוטלין, באמצעות ההצהרה של 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 היו מקורות לבאגים רבים. קוטלין מנסה להפחית באגים על ידי הוספת משתנים שאינם ניתנים לביטול.

שלב 1: מידע על יכולת ביטול

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

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

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

  • אפשר להתיר לרכיבי הרשימה להיות Null.
  • ניתן לאפשר לרשימה להיות ריקה, אך אם היא לא ריקה, היא לא תוכל להכיל ערך (null).
  • ניתן לאשר גם את הרשימה או את הרכיבים ללא ערך.

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

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

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

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

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

נקודה לגבי מצביעי null

אם את/ה ממש אוהב/ת את NullPointerExceptions, קוטלין מאפשרת לך לשמור אותם. האופרטור 'not-null' (!!) (double-bang) ממיר כל ערך לסוג שאינו Null, ומחריג אותו אם הערך הוא null.

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

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

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

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

  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. כדאי לנסות שילובים שונים של מערכים ורשימות מקוננות. כמו בשפות אחרות, אפשר למקם מערכים ורשימות ב-Nest. כלומר, כשמערך מערך, קיים מערך של מערך, ולא של מערך שטוח של תוכן המערך. הרכיבים של מערך יכולים להיות גם רשימות, והרכיבים של רשימות יכולים להיות מערכים.
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. כשאינכם משתמשים בשירות המערך כדי להדפיס אותו, קוטלין להדפיס את הכתובת במקום את תוכן המערך.

  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. ב-Cotlin, אפשר לעבור בין הרכיבים והאינדקסים בו-זמנית. דוגמה:
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. כדאי לנסות טווחים וגדלים שונים של שלבים. אפשר לציין טווחים של מספרים או תווים, בסדר אלפביתי. ובשפות אחרות, אינכם צריכים להתקדם ב-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. כדאי לנסות כמה לולאות. כמו בשפות אחרות, ל-Cotlin יש while לולאות, do...while לולאות ו-++ ואופרטורים --. בקוטלין יש גם 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 באופן מפורש.
  • אפשר להקצות משתנים עם val רק פעם אחת.
  • משתני Kotlin לא ניתנים לביטול כברירת מחדל. יש להשתמש ב-? כדי להפוך משתנים לאפס.
  • עם Kotlin, אפשר ללול בין האינדקס לרכיבים של מערך בו-זמנית בלולאה של for.

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

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

התיעוד של Kotlin

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

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

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

קורס של Udacity

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

IntelliJ IDEA

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

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

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

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

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

מענה על השאלות האלה

שאלה 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

4

38

357 עד 307

שאלה 3

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

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

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

הוספה לא ניתן להגדיר את המשתנה rocks כ-null.

הוספה של המשתנה rocks לא אמורה להיות מופעלת מיד.

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

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