改用新語言是一項艱鉅的任務。為確保團隊順利遷移,請緩慢起步,分批遷移,並頻繁測試。Kotlin 會編譯為 JVM 位元碼,並與 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 + '\'' +
'}';
}
}
您可以使用一行 Kotlin 取代 Java 類別,如下所示:
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 擴充功能函式,您就可以為 Java 呼叫端提供相同的 Utils 介面,同時為不斷擴大的 Kotlin 程式碼集提供更精簡的 API。
一開始,您可以先使用 IDE 提供的自動轉換功能,將這個 Utils 類別轉換為 Kotlin。輸出的範例大概如下:
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,且您已遷移較小的項目後,就可以繼續處理較大的元件,例如片段、活動、ViewModel 物件及與商業邏輯相關的其他類別。
注意事項
就像 Java 有特定的樣式,Kotlin 也有獨特的慣用樣式,使其簡明易用。不過,您一開始可能會發現,團隊編寫的 Kotlin 程式碼看起來與它取代的 Java 程式碼更像。隨著團隊使用 Kotlin 經驗日益增加,這種情況會慢慢改變。循序漸進是成功的關鍵。
當 Kotlin 程式碼集不斷擴大時,您可以採取下列做法來確保一致性:
常見的程式設計標準
在採用程序早期,請務必定義一組標準的程式設計慣例。您可以視情況偏離 Android 的 「Kotlin 樣式指南」。
靜態分析工具
請使用 Android lint 和其他靜態分析工具,強制執行團隊設定的程式設計標準。ktlint 是第三方 Kotlin Linter,也為 Kotlin 提供了其他規則。
持續整合
請務必遵守常用程式設計標準,並為您的 Kotlin 程式碼提供足夠的測試涵蓋範圍。在自動化建構程序中採用這個做法,有助於確保一致性和遵循這些標準。
互通性
Kotlin 大部分與 Java 順暢地互通,但請留意以下幾點。
是否可為空值
Kotlin 利用已編譯的程式碼中的是否可為空值註解,來推斷 Kotlin 端是否可為空值。如果未提供註解,Kotlin 預設為某種平台類型,即可視為可為空值或不可為空值的類型。如果未謹慎處理平台型別,可能會發生執行階段NullPointerException問題。
採用新功能
Kotlin 提供許多新程式庫和語法糖,藉此減少樣板,提升開發速度。即便如此,使用 Kotlin 的標準程式庫函式時請小心謹慎、有條不紊。這些函式包括集合函式、協同程式和lambda。
以下是 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() }
這個範例中的用意是,如果 nullableFoo 不是空值,則執行 foo.baz 和 foo.zap,因此避免使用 NullPointerException。雖然這個程式碼可以正常執行,但相較於空值檢查和智慧型層級轉換,這個程式碼沒那麼容易讀取,如以下範例所示:
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 選擇透過繼承提高撰寫速度。為簡化撰寫作業,Kotlin 內建支援,可透過委派實作行為。
此行為會為模擬架構 (例如 Mockito) 帶來問題。在測試期間,這些架構利用介面實作或繼承來覆寫行為。如要進行單元測試,您可以啟用 Mockito 的 Mock Maker Inline 功能,藉此模擬最終類別和方法。您也可以使用 All-Open 編譯器外掛程式,開啟要在編譯過程中測試的任何 Kotlin 類別及其成員。使用這個外掛程式的主要優點,是能用於單元測試和檢測設備測試。
更多資訊
如要進一步瞭解如何使用 Kotlin,請參閱下列連結:
- Android 以 Kotlin 優先的做法
- 「用於 Kotlin 入門的其他資源」
- 「其他供 Java 使用者學習 Kotlin 的資源」
- 「從 Java 到 Kotlin 學習課程」:一系列資源,可助 Java 程式設計師學習和編寫符合語言習慣的 Kotlin。