קוטלין היא שפת תכנות בשימוש נפוץ על ידי מפתחי Android בכל מקום. הנושא הזה משמש כ-Kotlin שיעזרו לכם להתחיל לעבוד במהירות.
הצהרה לגבי משתנים
Kotlin משתמשים בשתי מילות מפתח שונות כדי להצהיר על משתנים: val
ו-var
.
- משתמשים בפונקציה
val
למשתנה שהערך שלו אף פעם לא משתנה. לא ניתן להקצות מחדש ערך למשתנה שהוצהר באמצעותval
. - משתמשים בפונקציה
var
למשתנה שהערך שלו יכול להשתנות.
בדוגמה הבאה, count
הוא משתנה מסוג Int
שמוקצה לו
הערך הראשוני של 10
:
var count: Int = 10
Int
הוא סוג שמייצג מספר שלם, אחד מסוגי המספרים הרבים
שאפשר לייצג ב-Kotlin. בדומה לשפות אחרות, אפשר להשתמש גם ב-
Byte
, Short
, Long
, Float
ו-Double
בהתאם לנתונים המספריים שלך.
המשמעות של מילת המפתח var
היא שאפשר להקצות מחדש ערכים ל-count
לפי הצורך. עבור
לדוגמה, אפשר לשנות את הערך של count
מ-10
ל-15
:
var count: Int = 10
count = 15
עם זאת, יש ערכים שלא אמורים להשתנות. כדאי לשקול String
בשם
languageName
. כדי לוודא שהשדה languageName
תמיד מכיל ערך
של "Kotlin", לאחר מכן אפשר להצהיר על languageName
באמצעות מילת המפתח val
:
val languageName: String = "Kotlin"
מילות המפתח האלה מאפשרות לכם להסביר במפורש מה אפשר לשנות. משתמשים בהם כדי
את היתרון שלכם לפי הצורך. אם צריך לאפשר הקצאה מחדש של הפניה למשתנה,
להצהיר עליו בתור var
. אחרת, צריך להשתמש ב-val
.
סוג ההסקה
בהמשך לדוגמה הקודמת, כאשר הקציתם ערך ראשוני ל-
languageName
, המהדר של Kotlin יכול להסיק את הסוג על סמך סוג
בערך שהוקצה.
מכיוון שהערך של "Kotlin"
הוא מסוג String
, המהדר מסיק
languageName
הוא גם String
. שימו לב ש-Kotlin הוא סוג סטטי
בשפת היעד. זה אומר שהסוג מסתיים בזמן הידור
שינויים.
בדוגמה הבאה, languageName
נחשב ל-String
, כך שלא ניתן
מפעילים פונקציה שאינה חלק מהמחלקה String
:
val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()
// Fails to compile
languageName.inc()
toUpperCase()
היא פונקציה שניתן לקרוא לה רק על משתנים מסוג
String
. מכיוון שהמהדר של Kotlin הסיק ש-languageName
הוא String
,
אפשר להתקשר בבטחה אל toUpperCase()
. עם זאת, inc()
הוא אופרטור Int
ולכן לא ניתן לקרוא לה ב-String
. הגישה של Kotlin להקלדה
ההֶקֵּשׁ שלכם מספק גם תמציתיות וגם בטיחות בסוג.
בטיחות אפס
בשפות מסוימות אפשר להצהיר על משתנה של סוג הפניה בלי לספק ערך ראשוני מפורש. במקרים כאלה, המשתנים מכילים בדרך כלל ערך null עם ערך מסוים. משתני Kotlin לא יכולים להכיל ערכי null כברירת מחדל. המשמעות היא קטע הקוד הבא אינו חוקי:
// Fails to compile
val languageName: String = null
כדי שמשתנה יהיה ערך null, הוא צריך להיות מסוג null. אפשר
לציין משתנה כניתן ל-null על ידי סיומת הסוג שלו ב-?
, כפי שמוצג
בדוגמה הבאה:
val languageName: String? = null
בסוג String?
, אפשר להקצות ערך String
או null
languageName
עליך לטפל במשתנים שאינם יכולים להיות זהירים או לסכן אותם
NullPointerException
ב-Java, לדוגמה, אם מנסים להפעיל method
עם ערך null, התוכנית שלך קורסת.
Kotlin מספקת מספר מנגנונים לעבודה בטוחה עם ערכי null משתנים. מידע נוסף זמין במאמר הבא: דפוסי Kotlin נפוצים ב-Android: Nullability.
מותנים
ב-Kotlin יש כמה מנגנונים להטמעת לוגיקה מותנית. במידה הרבה ביותר
אחת מהאפשרויות הבאות היא הצהרת if-else. אם ביטוי מופיע בתוך
סוגריים ליד מילת מפתח if
מחזירה true
, והקוד בתוך
ההסתעפות הזאת (כלומר, הקוד שמופיע מיד אחרי עטוף מתולתל).
סוגריים מסולסלים). אחרת, הקוד בהסתעפות else
יריץ.
if (count == 42) {
println("I have the answer.")
} else {
println("The answer eludes me.")
}
אפשר לייצג כמה תנאים באמצעות else if
. כך אתם יכולים לייצג
לוגיקה מפורטת ומורכבת יותר בהצהרה מותנית אחת, כפי שמוצג
לדוגמה:
if (count == 42) {
println("I have the answer.")
} else if (count > 35) {
println("The answer is close.")
} else {
println("The answer eludes me.")
}
הצהרות מותנות הן שימושיות לייצוג לוגיקה של שמירת מצב, אבל אפשר
לגלות שאתם חוזרים על עצמכם כשאתם כותבים אותם. בדוגמה שלמעלה,
פשוט מדפיסים String
בכל הסתעפות. כדי להימנע מחזרה כזו, Kotlin
ביטויים מותנים. אפשר לשכתב את הדוגמה האחרונה באופן הבא:
val answerString: String = if (count == 42) {
"I have the answer."
} else if (count > 35) {
"The answer is close."
} else {
"The answer eludes me."
}
println(answerString)
במרומז, כל הסתעפות מותנית מחזירה את התוצאה של הביטוי
השורה האחרונה, כך שלא צריך להשתמש במילת מפתח return
. בגלל שהתוצאה של
כל שלוש ההסתעפויות הן מסוג String
, התוצאה של הביטוי if-else היא
גם מסוג String
. בדוגמה הזו, ל-answerString
מוקצית האות
מהתוצאה של הביטוי if-else. אפשר להשתמש בסוג ההֶקֵּשׁ,
השמטת את הצהרת הסוג המפורשת לגבי answerString
, אבל בדרך כלל מומלץ
לכלול אותה לשם הבהרה.
ככל שהמורכבות של ההצהרה המותנית גדלה, כדאי לנסות להחליף את הביטוי 'if-else' בביטוי מתי, כמו שהוא בדוגמה הבאה:
val answerString = when {
count == 42 -> "I have the answer."
count > 35 -> "The answer is close."
else -> "The answer eludes me."
}
println(answerString)
כל הסתעפות בביטוי when
מיוצגת על ידי תנאי, חץ
(->
) ותוצאה. אם התנאי שבצד שמאל של החץ
הפונקציה מחזירה את הערך True, אז התוצאה של הביטוי בצד ימין היא
הוחזרו. חשוב לשים לב שהביצוע לא עובר מהסתעפות אחת להסתעפות אחת לאחרת.
הקוד בדוגמה של הביטוי when
מקביל מבחינה פונקציונלית לקוד של הביטוי
את הדוגמה הקודמת, אבל נטען כי קל יותר לקרוא אותה.
התנאים של Kotlin מדגישים את אחת מהתכונות העוצמתיות יותר, העברה חכמה. במקום להשתמש באופרטור החיפוש הבטוח או באופרטור not-null את האופרטור 'טענת נכוֹנוּת' (assertion) כדי לעבוד עם ערכים שהם יכולים להיות null, ובמקום זאת אפשר לבדוק מכיל הפניה לערך null באמצעות הצהרה מותנית, שמוצגת בדוגמה הבאה:
val languageName: String? = null
if (languageName != null) {
// No need to write languageName?.toUpperCase()
println(languageName.toUpperCase())
}
בתוך ההסתעפות המותנה, ניתן להתייחס אל languageName
כאל ערך null.
Kotlin חכם מספיק כדי לזהות את התנאי לביצוע ההסתעפות.
הוא ש-languageName
לא מכיל ערך null, אז לא צריך להתייחס
הערך languageName
יכול להיות null בתוך הסתעפות הזו. ההעברה החכמה הזו פועלת במצב null
בדיקות,
type checks,
או כל תנאי שעונה על
חוזה.
פונקציות
אפשר לקבץ ביטוי אחד או יותר בפונקציה. במקום לחזור על הפעולה את אותה סדרה של ביטויים בכל פעם שצריכים תוצאה, אפשר לארוז את הביטויים בפונקציה ולקרוא לפונקציה הזו במקום זאת.
כדי להצהיר על פונקציה, צריך להשתמש במילת המפתח fun
ולאחר מכן בשם הפונקציה.
בשלב הבא מגדירים את סוגי הקלט שהפונקציה מקבלת, אם יש, ומצהירים עליה
סוג הפלט שהוא מחזיר. גוף הפונקציה הוא המקום שבו מגדירים
ביטויים שנקראים כשהפונקציה מופעלת.
בהמשך לדוגמאות הקודמות, הנה פונקציית Kotlin מלאה:
fun generateAnswerString(): String {
val answerString = if (count == 42) {
"I have the answer."
} else {
"The answer eludes me"
}
return answerString
}
השם של הפונקציה בדוגמה שלמעלה הוא generateAnswerString
. הוא
לא מקבל קלט. הפלט שלה הוא מסוג String
. כדי להתקשר
באמצעות השם שלה ואחריו אופרטור ההפעלה (()
). ב
בדוגמה הבאה, המשתנה answerString
מאותחל עם התוצאה של
generateAnswerString()
.
val answerString = generateAnswerString()
פונקציות יכולות לקבל ארגומנטים כקלט, כפי שמוצג בדוגמה הבאה:
fun generateAnswerString(countThreshold: Int): String {
val answerString = if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
return answerString
}
כשמצהירים על פונקציה, אפשר לציין כל מספר של ארגומנטים
שונים. בדוגמה שלמעלה, generateAnswerString()
מקבל ארגומנט אחד בשם
countThreshold
מסוג Int
. בתוך הפונקציה, אפשר
ארגומנט באמצעות שם שלו.
כשמפעילים את הפונקציה הזו צריך לכלול ארגומנט בתוך הפונקציה הסוגריים של הקריאה החוזרת (caller):
val answerString = generateAnswerString(42)
פישוט של הצהרות על פונקציות
generateAnswerString()
היא פונקציה פשוטה למדי. הפונקציה מצהירה
ואז חוזר מיד. כשהתוצאה של ביטוי יחיד היא
שמוחזר מפונקציה, אפשר לדלג על הצהרה על משתנה מקומי באופן ישיר
מחזירה את התוצאה של הביטוי if-else שנכלל בפונקציה, כפי
שמוצגת בדוגמה הבאה:
fun generateAnswerString(countThreshold: Int): String {
return if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
}
אפשר גם להחליף את מילת המפתח החוזרת באופרטור ההקצאה:
fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
"I have the answer"
} else {
"The answer eludes me"
}
פונקציות אנונימיות
לא לכל פונקציה צריך לתת שם. פונקציות מסוימות מזוהות באופן ישיר יותר באמצעות את הקלט והפלט שלהם. הפונקציות האלה נקראות פונקציות אנונימיות. שלך יכולה לשמור הפניה לפונקציה אנונימית, תוך שימוש בהפניה הזו קוראים לפונקציה האנונימית מאוחר יותר. אפשר גם להעביר את ההפניה בדומה לשאר סוגי העזר.
val stringLengthFunc: (String) -> Int = { input ->
input.length
}
בדומה לפונקציות בעלות שם, פונקציות אנונימיות יכולות להכיל כמה ביטויים שרוצים. הערך המוחזר של הפונקציה הוא התוצאה של הביטוי הסופי.
בדוגמה שלמעלה, stringLengthFunc
מכיל הפניה לקובץ אנונימי
פונקציה שמקבלת את הערך String
כקלט ומחזירה את אורך הקלט
String
כפלט מסוג Int
. לכן סוג הפונקציה הוא
מסומן בתור (String) -> Int
. עם זאת, הקוד הזה לא מפעיל את הפונקציה.
כדי לאחזר את התוצאה של הפונקציה, צריך להפעיל אותה כמו
פונקציה בעלת שם. חובה לספק String
כשמתקשרים אל stringLengthFunc
, כמו
שמוצגת בדוגמה הבאה:
val stringLengthFunc: (String) -> Int = { input ->
input.length
}
val stringLength: Int = stringLengthFunc("Android")
פונקציות בסדר גבוה יותר
פונקציה יכולה להשתמש בפונקציה אחרת כארגומנט. פונקציות שמשתמשות בפלטפורמה אחרת פונקציות כארגומנטים נקראות פונקציות מסדר גבוה יותר. הדפוס הזה שימושי לתקשורת בין רכיבים, באותו אופן שבו משתמשים ממשק קריאה חוזרת ב-Java.
דוגמה לפונקציה בסדר גבוה יותר:
fun stringMapper(str: String, mapper: (String) -> Int): Int {
// Invoke function
return mapper(str)
}
הפונקציה stringMapper()
לוקחת את String
עם פונקציה
נגזרת ערך Int
מ-String
שאתה מעביר אליו.
כדי לקרוא ל-stringMapper()
, אפשר להעביר את String
ואת הפונקציה
תואם לפרמטר הקלט השני, כלומר פונקציה שמקבלת את הערך String
כקלט ומפיק Int
, כמו בדוגמה הבאה:
stringMapper("Android", { input ->
input.length
})
אם הפונקציה האנונימית היא הפרמטר last שמוגדר בפונקציה, אפשר לבצע את הפעולות הבאות: מעבירים אותו מחוץ לסוגריים שמשמשים להפעלת הפונקציה, כמו שאפשר לראות בדוגמה הבאה:
stringMapper("Android") { input ->
input.length
}
פונקציות אנונימיות קיימות בספרייה הרגילה של Kotlin. עבור מידע נוסף: פונקציות בהזמנה גבוהה יותר ו-Lambdas
שיעורים
כל הסוגים שהוזכרו עד עכשיו מובנים בתכנות Kotlin
בשפת היעד. כדי להוסיף סוג מותאם אישית משלך, אפשר להגדיר כיתה
באמצעות מילת המפתח class
, כפי שמוצג בדוגמה הבאה:
class Car
מאפיינים
מחלקות מייצגות מצב באמצעות מאפיינים. א'
property הוא
משתנה ברמת המחלקה שיכול לכלול מקש getter, רכיב מגדיר (Setter) ושדה גיבוי (backup withholding).
יש ברכב צורך בגלגלים כדי לנסוע, לכן אפשר להוסיף רשימה של Wheel
אובייקטים
של Car
, כמו בדוגמה הבאה:
class Car {
val wheels = listOf<Wheel>()
}
חשוב לשים לב ש-wheels
הוא public val
, כלומר אפשר לגשת ל-wheels
מ-
מחוץ לכיתה Car
, ואי אפשר להקצות אותה מחדש. אם רוצים לקבל
של Car
, קודם צריך לקרוא ל-constructor שלו. בדף הזה אתם יכולים
לגשת לכל אחד מהנכסים הנגישים שלו.
val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car
אם ברצונך להתאים אישית את הגלגלים, תוכל להגדיר בנאי מותאם אישית מציין את אופן האתחול של מאפייני הכיתה:
class Car(val wheels: List<Wheel>)
בדוגמה שלמעלה, ה-constructor של המחלקה לוקח את List<Wheel>
בתור
של constructor ומשתמשת בארגומנט הזה כדי לאתחל את ה-wheels
שלו
לנכס.
פונקציות Class וencapsulation
הכיתות משתמשות בפונקציות כדי לבנות מודלים של התנהגות. פונקציות יכולות לשנות את המצב, כדי לעזור לך כדי לחשוף רק את הנתונים שרוצים לחשוף. בקרת הגישה הזו היא חלק מ קונספט גדול יותר שמתמקד באובייקטים ונקרא אנקפסולציה.
בדוגמה הבאה, הנכס doorLock
נשאר פרטי מכל דבר
מחוץ לכיתה Car
. כדי לפתוח את הרכב, צריך להתקשר למוקד של unlockDoor()
הפונקציה מעבירה מפתח חוקי, כמו בדוגמה הבאה:
class Car(val wheels: List<Wheel>) {
private val doorLock: DoorLock = ...
fun unlockDoor(key: Key): Boolean {
// Return true if key is valid for door lock, false otherwise
}
}
אם רוצים להתאים אישית את אופן ההפניה לנכס, אפשר לציין
מותאם אישית ומפענח. לדוגמה, אם אתם רוצים לחשוף נכס
תוך הגבלת הגישה לרכיב המגדיר שלו, אפשר להגדיר אותו בתור
private
:
class Car(val wheels: List<Wheel>) {
private val doorLock: DoorLock = ...
var gallonsOfFuelInTank: Int = 15
private set
fun unlockDoor(key: Key): Boolean {
// Return true if key is valid for door lock, false otherwise
}
}
באמצעות שילוב של מאפיינים ופונקציות, אפשר ליצור מחלקות של כל סוגי האובייקטים.
יכולת פעולה הדדית
אחת התכונות החשובות ביותר של Kotlin היא יכולת הפעולה ההדדית הנוזלית שלו עם Java. מכיוון שהקוד של Kotlin עובר הידור של bytecode של JVM, קוד Kotlin שלך יכול לקרוא ישירות לקוד Java ולהיפך. כלומר, אתם יכולים למנף קיימות של ספריות Java ישירות מ-Kotlin. בנוסף, רוב ממשקי API של Android נכתבים ב-Java, וניתן לקרוא להם ישירות מ-Kotlin.
השלבים הבאים
Kotlin היא שפה גמישה ופרגמטית עם תמיכה מוגדלת ומומנטום. רביעי אם עדיין לא עשיתם זאת, מומלץ לנסות זאת. כדאי לעיין בשלבים הבאים במסמכים הרשמיים של Kotlin יחד עם המדריך כיצד להגיש בקשה דפוסי Kotlin נפוצים באפליקציות ל-Android.