מעבר לשפה חדשה יכול להיות משימה מאתגרת. המתכון להצלחה הוא להתחיל לאט, להתקדם בחלקים ולבצע בדיקות לעיתים קרובות כדי שהצוות יפעל בצורה שתביא להצלחה. קל לבצע מיגרציה באמצעות Kotlin, כי היא עוברת קומפילציה ל-JVM bytecode ויש לה יכולת פעולה הדדית מלאה עם Java.
בניית הצוות
השלב הראשון לפני ההעברה הוא לוודא שלצוות יש בסיס משותף של ידע. ריכזנו כאן כמה טיפים שיעזרו לכם להאיץ את תהליך הלמידה של הצוות.
יצירת קבוצות למידה
קבוצות לימוד הן דרך יעילה להקל על הלמידה והשינון. מחקרים מראים ששינון של מה שלמדתם בקבוצה עוזר לחזק את החומר. קונים ספר על Kotlin או חומר לימוד אחר לכל חבר בקבוצה, ומבקשים מהקבוצה לעבור על כמה פרקים בכל שבוע. בכל מפגש, הקבוצה צריכה להשוות את מה שהיא למדה ולדון בשאלות או בתצפיות.
בניית תרבות של הוראה
לא כולם רואים את עצמם כמורים, אבל כל אחד יכול ללמד. ממנהל טכנולוגיה או ראש צוות ועד משתתף יחיד, כל אחד יכול לעודד סביבת למידה שתעזור להבטיח הצלחה. אחת הדרכים לעשות את זה היא לקיים מצגות תקופתיות שבהן אדם אחד בצוות מציג משהו שהוא למד או רוצה לשתף. אתם יכולים להיעזר בקבוצת הלימוד ולבקש מתנדבים שיציגו פרק חדש בכל שבוע, עד שהצוות ירגיש בנוח עם השפה.
מינוי אלופים
לבסוף, צריך למנות אלוף שיעמוד בראש מאמצי הלמידה. האדם הזה יכול לשמש כמומחה בתחום (SME) כשמתחילים בתהליך ההטמעה. חשוב לכלול את האדם הזה בכל פגישות התרגול שקשורות ל-Kotlin. הכי טוב אם לאדם הזה כבר יש תשוקה ל-Kotlin וידע מעשי כלשהו.
שילוב הדרגתי
חשוב להתחיל לאט ולחשוב בצורה אסטרטגית על החלקים במערכת האקולוגית שצריך להעביר קודם. לרוב, עדיף לבצע את הבידוד הזה באפליקציה אחת בארגון ולא באפליקציה מרכזית. בכל הנוגע להעברת האפליקציה שנבחרה, כל מקרה הוא שונה, אבל הנה כמה מקומות נפוצים להתחיל מהם.
מודל נתונים
מודל הנתונים שלכם כנראה מורכב מהרבה נתוני מצב, וגם מכמה שיטות. יכול להיות שבמודל הנתונים יהיו גם שיטות נפוצות כמו toString(),
equals() ו-hashcode(). בדרך כלל אפשר להעביר את השיטות האלה ולבדוק אותן בקלות בנפרד.
לדוגמה, נניח את קטע הקוד הבא ב-Java:
public class Person {
private String firstName;
private String lastName;
// ...
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(firstName, person.firstName) &&
Objects.equals(lastName, person.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
@Override
public String toString() {
return "Person{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
אפשר להחליף את מחלקת Java בשורה אחת של Kotlin, כמו שמוצג כאן:
data class Person(var firstName: String?, var lastName: String?)
לאחר מכן אפשר לבצע בדיקות יחידה של הקוד מול חבילת הבדיקות הנוכחית. הרעיון הוא להתחיל עם מודל אחד בכל פעם ולעבור בין מחלקות שהן בעיקר מצב ולא התנהגות. חשוב לבצע בדיקות לעיתים קרובות במהלך התהליך.
העברת בדיקות
אפשרות נוספת היא להמיר בדיקות קיימות ולהתחיל לכתוב בדיקות חדשות ב-Kotlin. כך הצוות שלכם יוכל להתרגל לשפה לפני שתכתבו קוד שאתם מתכננים לשלב באפליקציה.
העברת שיטות עזר לפונקציות של תוספים
אפשר לייצג כל מחלקה סטטית של כלי עזר (StringUtils, IntegerUtils, DateUtils, YourCustomTypeUtils וכו') כפונקציות הרחבה של Kotlin ולהשתמש בהן בבסיס הקוד הקיים של Java.
לדוגמה, נניח שיש לכם מחלקה StringUtils עם כמה שיטות:
package com.java.project;
public class StringUtils {
public static String foo(String receiver) {
return receiver...; // Transform the receiver in some way
}
public static String bar(String receiver) {
return receiver...; // Transform the receiver in some way
}
}
אחר כך אפשר להשתמש בשיטות האלה במקומות אחרים באפליקציה, כמו בדוגמה הבאה:
...
String myString = ...
String fooString = StringUtils.foo(myString);
...
באמצעות פונקציות הרחבה של Kotlin, אתם יכולים לספק את אותו ממשק Utils למבצעי קריאות ב-Java, ובמקביל להציע API תמציתי יותר לבסיס הקוד של Kotlin שמתרחב.
כדי לעשות את זה, אפשר להתחיל בהמרת המחלקה Utils הזו ל-Kotlin באמצעות ההמרה האוטומטית שמספק סביבת הפיתוח המשולבת (IDE). פלט לדוגמה:
package com.java.project
object StringUtils {
fun foo(receiver: String): String {
return receiver...; // Transform the receiver in some way
}
fun bar(receiver: String): String {
return receiver...; // Transform the receiver in some way
}
}
לאחר מכן, מסירים את ההגדרה של המחלקה או האובייקט, מוסיפים לכל שם פונקציה את הסוג שאליו הפונקציה הזו צריכה להתייחס, ומשתמשים בזה כדי להפנות לסוג בתוך הפונקציה, כמו בדוגמה הבאה:
package com.java.project
fun String.foo(): String {
return this...; // Transform the receiver in some way
}
fun String.bar(): String {
return this...; // Transform the receiver in some way
}
לבסוף, מוסיפים הערה מסוג JvmName לחלק העליון של קובץ המקור כדי שהשם המהודר יהיה תואם לשאר האפליקציה, כמו בדוגמה הבאה:
@file:JvmName("StringUtils")
package com.java.project
...
הגרסה הסופית אמורה להיראות כך:
@file:JvmName("StringUtils")
package com.java.project
fun String.foo(): String {
return this...; // Transform `this` string in some way
}
fun String.bar(): String {
return this...; // Transform `this` string in some way
}
שימו לב שאפשר עכשיו להפעיל את הפונקציות האלה באמצעות Java או Kotlin עם מוסכמות שמתאימות לכל שפה.
Kotlin
// ... val myString: String = "hello" val fooString = myString.foo() // ...
Java
... String myString = ... String fooString = StringUtils.foo(myString); ...
השלמת ההעברה
אחרי שהצוות שלכם ירגיש בנוח עם Kotlin ותסיימו להעביר אזורים קטנים יותר, תוכלו להתקדם לטיפול ברכיבים גדולים יותר כמו fragments, activities, ViewModel objects ו-class אחרים שקשורים ללוגיקה עסקית.
שיקולים
בדומה ל-Java, ל-Kotlin יש סגנון אידיומטי משלה שמאפשר לכתוב קוד תמציתי. עם זאת, יכול להיות שבהתחלה קוד Kotlin שהצוות שלכם יפיק ייראה יותר כמו קוד Java שהוא מחליף. הערך הזה משתנה לאורך זמן ככל שהניסיון של הצוות ב-Kotlin גדל. חשוב לזכור שהמפתח להצלחה הוא שינוי הדרגתי.
כדי לשמור על עקביות ככל שבסיס הקוד של Kotlin גדל, אפשר לבצע את הפעולות הבאות:
תקני תכנות נפוצים
חשוב להגדיר קבוצה סטנדרטית של מוסכמות תכנות בשלב מוקדם בתהליך ההטמעה. אפשר לחרוג ממדריך הסגנון של Kotlin ל-Android במקרים שבהם זה הגיוני.
כלים לניתוח סטטי
אפשר לאכוף את תקני הקידוד שנקבעו לצוות באמצעות Android lint וכלי ניתוח סטטיים אחרים. klint, כלי של צד שלישי לניתוח קוד Kotlin, מספק גם כללים נוספים ל-Kotlin.
אינטגרציה רציפה (CI)
חשוב להקפיד על תקני קידוד נפוצים ולספק כיסוי מספיק של בדיקות לקוד Kotlin. אם משלבים את השלב הזה בתהליך אוטומטי של בניית גרסה, אפשר לוודא שהתהליך עקבי ושעומד בתקנים האלה.
יכולת פעולה הדדית
ברוב המקרים, Kotlin פועלת בצורה חלקה עם Java, אבל חשוב לשים לב לנקודות הבאות.
מאפיין המציין אם ערך יכול להיות ריק (nullability)
Kotlin מסתמכת על הערות בנוגע לאפשרות של ערך null בקוד שעבר קומפילציה כדי להסיק את האפשרות של ערך null בצד של Kotlin. אם לא מספקים הערות, Kotlin משתמשת כברירת מחדל בסוג פלטפורמה שאפשר להתייחס אליו כסוג שניתן להגדרה כ-nullable או כ-non-nullable. אם לא מטפלים בבעיה הזו בזהירות, היא עלולה לגרום לבעיות בזמן הריצה של NullPointerException.
אימוץ תכונות חדשות
Kotlin מספקת הרבה ספריות חדשות וסוכר תחבירי כדי לצמצם את הקוד הסטנדרטי, וכך עוזרת להגדיל את מהירות הפיתוח. עם זאת, חשוב לנקוט משנה זהירות ולפעול בצורה שיטתית כשמשתמשים בפונקציות של הספרייה הסטנדרטית של Kotlin, כמו פונקציות של אוספים, קורוטינות וביטויי למבדה.
זוהי מלכודת נפוצה מאוד שמפתחי Kotlin חדשים נתקלים בה. נניח שיש את קוד Kotlin הבא:
val nullableFoo: Foo? = Foo() // This lambda executes only if nullableFoo is not null // and `foo` is of the non-nullable Foo type nullableFoo?.let { foo -> foo.baz() foo.zap() }
בדוגמה הזו, הכוונה היא להריץ את foo.baz() ואת foo.zap() אם nullableFoo לא ריק, וכך להימנע מ-NullPointerException. הקוד הזה פועל כמו שצריך, אבל קשה יותר לקרוא אותו מאשר בדיקת ערך null פשוטה והמרת טיפוס חכמה, כמו בדוגמה הבאה:
val nullableFoo: Foo? = null if (nullableFoo != null) { nullableFoo.baz() // Using !! or ?. isn't required; the Kotlin compiler infers non-nullability nullableFoo.zap() // from guard condition; smart casts nullableFoo to Foo inside this block }
בדיקה
ב-Kotlin, כברירת מחדל, מחלקות והפונקציות שלהן סגורות להרחבה. צריך לפתוח במפורש את המחלקות והפונקציות שרוצים ליצור להן מחלקת משנה. ההתנהגות הזו היא החלטה לגבי עיצוב השפה, שנבחרה כדי לקדם קומפוזיציה על פני ירושה. ל-Kotlin יש תמיכה מובנית בהטמעת התנהגות באמצעות העברה, כדי לפשט את הקומפוזיציה.
ההתנהגות הזו יוצרת בעיה במסגרות לזיוף אובייקטים (mocking), כמו Mockito, שמסתמכות על הטמעה או על ירושה של ממשק כדי לבטל התנהגויות במהלך הבדיקה. בבדיקות יחידה, אפשר להפעיל את התכונה Mock Maker Inline של Mockito, שמאפשרת ליצור מוקאפים של מחלקות ושיטות סופיות. לחלופין, אפשר להשתמש בתוסף All-Open של מהדר כדי לפתוח כל מחלקה של Kotlin ואת החברים שלה שרוצים לבדוק כחלק מתהליך ההידור. היתרון העיקרי בשימוש בתוסף הזה הוא שהוא פועל גם עם בדיקות יחידה וגם עם בדיקות מבוססות-מכשיר.
מידע נוסף
מידע נוסף על השימוש ב-Kotlin זמין בקישורים הבאים:
- הגישה של Android שמתמקדת ב-Kotlin
- משאבים שיעזרו לכם להתחיל לעבוד עם Kotlin
- מקורות מידע למשתמשי Java שרוצים ללמוד Kotlin
- תוכנית לימודים בנושא Java ל-Kotlin, אוסף של משאבים שעוזרים למתכנתי Java ללמוד ולכתוב Kotlin אידיומטית.