在本程式碼研究室中,您將瞭解如何將 Java 程式碼轉換為 Kotlin。此外,您也將學習 Kotlin 語言慣例,以及如何確保自己編寫的程式碼符合這些慣例。
本程式碼研究室適合使用 Java 的開發人員,他們正考慮將專案遷移至 Kotlin。首先,我們會使用 IDE 將幾個 Java 類別轉換為 Kotlin。接著,我們將查看轉換後的程式碼,瞭解如何讓程式碼更道地,並避免常見的陷阱。
課程內容
您將瞭解如何將 Java 轉換為 Kotlin。過程中,您將瞭解下列 Kotlin 語言功能和概念:
- 處理是否可為空值
- 實作單例項
- 資料類別
- 處理字串
- Elvis 運算子
- 解構
- 屬性和支援屬性
- 預設引數和具名參數
- 使用集合
- 擴充功能函式
- 頂層函式和參數
let
、apply
、with
和run
關鍵字
假設
您應已熟悉 Java。
軟硬體需求
建立新專案
如果您使用 IntelliJ IDEA,請建立含有 Kotlin/JVM 的新 Java 專案。
如果您使用 Android Studio,請建立沒有 Activity 的新專案。
代碼
我們會建立 User
模型物件和 Repository
單例類別,以便處理 User
物件,並公開使用者清單和格式化使用者名稱。
在 app/java/<yourpackagename> 下建立名為 User.java
的新檔案,然後貼入下列程式碼:
public class User {
@Nullable
private String firstName;
@Nullable
private String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = 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;
}
}
視專案類型而定,如果您使用 Android 專案,請匯入 androidx.annotation.Nullable
;否則請匯入 org.jetbrains.annotations.Nullable
。
建立名為 Repository.java
的新檔案,然後貼入下列程式碼:
import java.util.ArrayList;
import java.util.List;
public class Repository {
private static Repository INSTANCE = null;
private List<User> users = null;
public static Repository getInstance() {
if (INSTANCE == null) {
synchronized (Repository.class) {
if (INSTANCE == null) {
INSTANCE = new Repository();
}
}
}
return INSTANCE;
}
// keeping the constructor private to enforce the usage of getInstance
private Repository() {
User user1 = new User("Jane", "");
User user2 = new User("John", null);
User user3 = new User("Anne", "Doe");
users = new ArrayList();
users.add(user1);
users.add(user2);
users.add(user3);
}
public List<User> getUsers() {
return users;
}
public List<String> getFormattedUserNames() {
List<String> userNames = new ArrayList<>(users.size());
for (User user : users) {
String name;
if (user.getLastName() != null) {
if (user.getFirstName() != null) {
name = user.getFirstName() + " " + user.getLastName();
} else {
name = user.getLastName();
}
} else if (user.getFirstName() != null) {
name = user.getFirstName();
} else {
name = "Unknown";
}
userNames.add(name);
}
return userNames;
}
}
我們的 IDE 可以自動將 Java 程式碼重構為 Kotlin 程式碼,但有時需要一些協助。我們會先這麼做,然後逐步瞭解重構後的程式碼,瞭解重構方式和原因。
前往 User.java
檔案並轉換為 Kotlin:依序點選「選單列」->「程式碼」->「將 Java 檔案轉換為 Kotlin 檔案」。
如果 IDE 在轉換後提示修正,請按一下「Yes」(是)。
您應該會看到下列 Kotlin 程式碼:
class User(var firstName: String?, var lastName: String?)
請注意,User.java
已重新命名為 User.kt
。Kotlin 檔案的副檔名為 .kt。
在 Java User
類別中,我們有兩個屬性:firstName
和 lastName
。每個屬性都有 getter 和 setter 方法,因此值可變動。Kotlin 的可變動變數關鍵字是 var
,因此轉換工具會為每個屬性使用 var
。如果 Java 屬性只有 getter,則會是不可變動的屬性,並會宣告為 val
變數。val
類似於 Java 中的 final
關鍵字。
Kotlin 和 Java 的主要差異之一,在於 Kotlin 會明確指定變數是否可接受空值。方法是在型別宣告中附加 `?
`。
由於我們將 firstName
和 lastName
標示為可為空值,自動轉換器會使用 String?
自動將屬性標示為可為空值。如果您使用 org.jetbrains.annotations.NotNull
或 androidx.annotation.NonNull
將 Java 成員註解為非空值,轉換器會辨識出這點,並在 Kotlin 中將欄位設為非空值。
基本重構已完成。但我們可以採用更慣用的方式編寫這項內容。現在就來一探究竟。
資料類別
我們的 User
類別只保留資料。Kotlin 有一個專門用於這類類別的關鍵字:data
。只要將這個類別標示為 data
類別,編譯器就會自動為我們建立 getter 和 setter。系統也會衍生 equals()
、hashCode()
和 toString()
函式。
讓我們將 data
關鍵字新增至 User
類別:
data class User(var firstName: String, var lastName: String)
與 Java 類似,Kotlin 可以有一個主要建構函式和一或多個次要建構函式。上述範例中的建構函式是 User 類別的主要建構函式。如果您要轉換有多個建構函式的 Java 類別,轉換器也會自動在 Kotlin 中建立多個建構函式。這些類別是以 constructor
關鍵字定義。
如要建立這個類別的例項,可以執行下列動作:
val user1 = User("Jane", "Doe")
Equality
Kotlin 具有兩種類型的等式:
- 結構等式會使用
==
運算子並呼叫equals()
,藉此判斷兩個例項是否相等。 - 參照等式會使用
===
運算子,藉此檢查兩個參照是否指向同一個物件。
在資料類別主要建構函式中定義的屬性,可用於結構等式檢查。
val user1 = User("Jane", "Doe")
val user2 = User("Jane", "Doe")
val structurallyEqual = user1 == user2 // true
val referentiallyEqual = user1 === user2 // false
在 Kotlin 中,我們可以為函式呼叫中的引數指派預設值。如果省略引數,系統會使用預設值。在 Kotlin 中,建構函式也是函式,因此我們可以透過預設引數,將 lastName
的預設值指定為 null
。如要這麼做,只要將 null
指派給 lastName
即可。
data class User(var firstName: String?, var lastName: String? = null)
// usage
val jane = User("Jane") // same as User("Jane", null)
val joe = User("John", "Doe")
呼叫函式時,可以為函式參數命名:
val john = User(firstName = "John", lastName = "Doe")
在另一個使用案例中,假設 firstName
的預設值為 null
,而 lastName
沒有預設值。在本例中,由於預設參數會先於沒有預設值的參數,因此您必須使用命名引數呼叫函式:
data class User(var firstName: String? = null, var lastName: String?)
// usage
val jane = User(lastName = "Doe") // same as User(null, "Doe")
val john = User("John", "Doe")
繼續操作前,請確認 User
類別是 data
類別。我們將 Repository
類別轉換為 Kotlin。自動轉換結果應如下所示:
import java.util.*
class Repository private constructor() {
private var users: MutableList<User?>? = null
fun getUsers(): List<User?>? {
return users
}
val formattedUserNames: List<String?>
get() {
val userNames: MutableList<String?> =
ArrayList(users!!.size)
for (user in users) {
var name: String
name = if (user!!.lastName != null) {
if (user!!.firstName != null) {
user!!.firstName + " " + user!!.lastName
} else {
user!!.lastName
}
} else if (user!!.firstName != null) {
user!!.firstName
} else {
"Unknown"
}
userNames.add(name)
}
return userNames
}
companion object {
private var INSTANCE: Repository? = null
val instance: Repository?
get() {
if (INSTANCE == null) {
synchronized(Repository::class.java) {
if (INSTANCE == null) {
INSTANCE =
Repository()
}
}
}
return INSTANCE
}
}
// keeping the constructor private to enforce the usage of getInstance
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList<Any?>()
users.add(user1)
users.add(user2)
users.add(user3)
}
}
我們來看看自動轉換器做了什麼:
- 新增了
init
區塊 (Repository.kt#L50) static
欄位現在是companion object
區塊的一部分 (Repository.kt#L33)- 由於物件並未在宣告時例項化 (Repository.kt#L7),因此
users
清單可為空值。 getFormattedUserNames()
方法現在是名為formattedUserNames
的屬性 (Repository.kt#L11)- 使用者清單的疊代 (最初是
getFormattedUserNames(
的一部分) 與 Java 的語法不同 (Repository.kt#L15)。
在繼續之前,我們先稍微清理一下程式碼。我們可以發現,轉換器將 users
清單轉換為可變動清單,其中包含可為空值的物件。雖然清單確實可以為空值,但假設清單無法保留空值使用者。因此請執行下列操作:
- 在
users
型別宣告中,移除User?
內的?
getUsers
應會傳回List<User>?
自動轉換器也會不必要地將使用者變數和 init 區塊中定義的變數宣告分成 2 行。我們將每個變數宣告放在一行。程式碼應如下所示:
class Repository private constructor() {
private var users: MutableList<User>? = null
fun getUsers(): List<User>? {
return users
}
val formattedUserNames: List<String?>
get() {
val userNames: MutableList<String?> =
ArrayList(users!!.size)
for (user in users) {
var name: String
name = if (user!!.lastName != null) {
if (user!!.firstName != null) {
user!!.firstName + " " + user!!.lastName
} else {
user!!.lastName
}
} else if (user!!.firstName != null) {
user!!.firstName
} else {
"Unknown"
}
userNames.add(name)
}
return userNames
}
companion object {
private var INSTANCE: Repository? = null
val instance: Repository?
get() {
if (INSTANCE == null) {
synchronized(Repository::class.java) {
if (INSTANCE == null) {
INSTANCE =
Repository()
}
}
}
return INSTANCE
}
}
// keeping the constructor private to enforce the usage of getInstance
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList<Any?>()
users.add(user1)
users.add(user2)
users.add(user3)
}
}
Init block
在 Kotlin 中,主要建構函式不得包含任何程式碼,因此初始化程式碼會放在 init
區塊中。功能相同。
class Repository private constructor() {
...
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList<Any?>()
users.add(user1)
users.add(user2)
users.add(user3)
}
}
init
程式碼的大部分內容都是用來初始化屬性。也可以在屬性宣告中完成這項操作。舉例來說,在 Repository
類別的 Kotlin 版本中,我們看到使用者屬性已在宣告中初始化。
private var users: MutableList<User>? = null
Kotlin 的 static
屬性和方法
在 Java 中,我們會使用 static
關鍵字表示欄位或函式屬於類別,但不屬於類別的例項。因此,我們在 Repository
類別中建立了 INSTANCE
靜態欄位。Kotlin 的對應項目是 companion object
區塊。您也會在這裡宣告靜態欄位和靜態函式。轉換器已建立並將 INSTANCE
欄位移至此處。
處理單例
由於我們只需要一個 Repository
類別的執行個體,因此我們在 Java 中使用了單例模式。使用 Kotlin 時,您可以將 class
關鍵字替換為 object
,在編譯器層級強制執行此模式。
移除私有建構函式和伴生物件,並將類別定義替換為 object Repository
。
object Repository {
private var users: MutableList<User>? = null
fun getUsers(): List<User>? {
return users
}
val formattedUserNames: List<String>
get() {
val userNames: MutableList<String> =
ArrayList(users!!.size)
for (user in users) {
var name: String
name = if (user!!.lastName != null) {
if (user!!.firstName != null) {
user!!.firstName + " " + user!!.lastName
} else {
user!!.lastName
}
} else if (user!!.firstName != null) {
user!!.firstName
} else {
"Unknown"
}
userNames.add(name)
}
return userNames
}
// keeping the constructor private to enforce the usage of getInstance
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList<Any?>()
users.add(user1)
users.add(user2)
users.add(user3)
}
}
使用 object
類別時,我們只需直接在物件上呼叫函式和屬性,如下所示:
val users = Repository.users
解構
Kotlin 允許使用稱為「解構宣告」的語法,將物件解構為多個變數。我們建立多個變數,並可獨立使用。
舉例來說,資料類別支援解構,因此我們可以在 for
迴圈中將 User
物件解構為 (firstName, lastName)
。這樣我們就能直接使用 firstName
和 lastName
值。請更新 for
迴圈,如下所示:
for ((firstName, lastName) in users!!) {
val name: String?
if (lastName != null) {
if (firstName != null) {
name = "$firstName $lastName"
} else {
name = lastName
}
} else if (firstName != null) {
name = firstName
} else {
name = "Unknown"
}
userNames.add(name)
}
將 Repository
類別轉換為 Kotlin 時,自動轉換器會將使用者清單設為可為空值,因為清單在宣告時並未初始化為物件。對於 users
物件的所有用法,都會使用非空值斷言運算子 !!
。這個運算子會將任何變數轉換為非空值類型。如果值為空值,則擲回例外狀況。使用 !!
時,您可能會在執行階段擲回例外狀況。
建議改用下列其中一種方法處理可為空值的情況:
- 執行空值檢查 (
if (users != null) {...}
) - 使用 elvis 運算子
?:
(程式碼研究室後續會說明) - 使用部分 Kotlin 標準函式 (本程式碼研究室稍後會介紹)
在本例中,我們知道使用者清單不需要可為空值,因為清單會在物件建構完成後立即初始化,因此我們可以在宣告物件時直接例項化。
建立集合型別的例項時,Kotlin 提供多個輔助函式,讓程式碼更易於閱讀及更具彈性。這裡我們使用 MutableList
做為 users
:
private var users: MutableList<User>? = null
為求簡單,我們可以採用 mutableListOf()
函式、提供清單元素型別、從 init
區塊中移除 ArrayList
建構函式呼叫,以及移除 users
屬性的明確型別宣告。
private val users = mutableListOf<User>()
我們也將 var 變更為 val,因為使用者會包含對使用者清單的不變動參照。請注意,參照是不可變動的,但清單本身是可變動的 (您可以新增或移除元素)。
完成這些變更後,我們的 users
屬性現在為非空值,我們可以移除所有不必要的 !!
運算子出現位置。
val userNames: MutableList<String?> = ArrayList(users.size)
for ((firstName, lastName) in users) {
...
}
此外,由於使用者變數已初始化,我們必須從 init
區塊中移除初始化作業:
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users.add(user1)
users.add(user2)
users.add(user3)
}
由於 lastName
和 firstName
都可以是 null
,因此在建構格式化使用者名稱清單時,我們需要處理可為空值的問題。由於我們希望在缺少任一名稱時顯示 "Unknown"
,因此可以從型別宣告中移除 ?
,讓名稱成為非空值。
val name: String
如果 lastName
為空值,則 name
為 firstName
或 "Unknown"
:
if (lastName != null) {
if (firstName != null) {
name = "$firstName $lastName"
} else {
name = lastName
}
} else if (firstName != null) {
name = firstName
} else {
name = "Unknown"
}
使用elvis 運算子 ?:
,可以更慣用的方式編寫這項程式碼。如果 elvis 運算子左側的運算式不是空值,則會傳回該運算式;如果左側是空值,則會傳回右側的運算式。
因此,如果下列程式碼不是空值,就會傳回 user.firstName
。如果 user.firstName
是空值,運算式會傳回右側的值 "Unknown"
:
if (lastName != null) {
...
} else {
name = firstName ?: "Unknown"
}
Kotlin 可透過字串範本輕鬆處理 String
。字串範本可讓您在字串宣告中參照變數。
自動轉換器已更新名字和姓氏的串連,直接在字串中參照變數名稱 (使用 $
符號),並將運算式放在 { }
之間。
// Java
name = user.getFirstName() + " " + user.getLastName();
// Kotlin
name = "${user.firstName} ${user.lastName}"
在程式碼中,將字串串連替換為:
name = "$firstName $lastName"
在 Kotlin 中,if
、when
、for
和 while
都是運算式,會傳回值。IDE 甚至會顯示警告,指出應將指派作業從 if
中移除:
我們按照 IDE 的建議,將指派作業從兩個 if
陳述式中移出。系統會指派 if 陳述式的最後一行。這樣一來,這個區塊的唯一用途就是初始化名稱值,就更清楚了:
name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName
}
} else {
firstName ?: "Unknown"
}
接著,我們會收到警告,指出 name
宣告可以與指派作業合併。我們也來套用這個設定。由於可以推斷名稱變數的型別,因此我們可以移除明確的型別宣告。現在,我們的 formattedUserNames
看起來會像這樣:
val formattedUserNames: List<String?>
get() {
val userNames: MutableList<String?> = ArrayList(users.size)
for ((firstName, lastName) in users) {
val name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName
}
} else {
firstName ?: "Unknown"
}
userNames.add(name)
}
return userNames
}
讓我們進一步瞭解 formattedUserNames
getter,看看如何讓它更符合慣用語。目前程式碼會執行下列作業:
- 建立新的字串清單
- 逐一查看使用者清單
- 根據使用者的姓名,為每位使用者建構格式化名稱
- 傳回新建立的清單
val formattedUserNames: List<String>
get() {
val userNames = ArrayList<String>(users.size)
for ((firstName, lastName) in users) {
val name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName
}
} else {
firstName ?: "Unknown"
}
userNames.add(name)
}
return userNames
}
Kotlin 提供豐富的集合轉換清單,可擴充 Java Collections API 的功能,加快開發速度並提升安全性。其中一個是 map
函式。這個函式會傳回新清單,其中包含將指定轉換函式套用至原始清單中每個元素的結果。因此,我們可以使用 map
函式,將 for
迴圈中的邏輯移至 map
主體內,不必建立新清單並手動逐一處理使用者清單。根據預設,map
中使用的目前清單項目名稱為 it
,但為了方便閱讀,您可以將 it
替換為自己的變數名稱。在本例中,我們將其命名為 user
:
val formattedUserNames: List<String>
get() {
return users.map { user ->
val name = if (user.lastName != null) {
if (user.firstName != null) {
"${user.firstName} ${user.lastName}"
} else {
user.lastName ?: "Unknown"
}
} else {
user.firstName ?: "Unknown"
}
name
}
}
為進一步簡化,我們可以完全移除 name
變數:
val formattedUserNames: List<String>
get() {
return users.map { user ->
if (user.lastName != null) {
if (user.firstName != null) {
"${user.firstName} ${user.lastName}"
} else {
user.lastName ?: "Unknown"
}
} else {
user.firstName ?: "Unknown"
}
}
}
我們發現自動轉換器將 getFormattedUserNames()
函式替換為名為 formattedUserNames
的屬性,該屬性具有自訂 getter。在幕後,Kotlin 仍會產生傳回 List
的 getFormattedUserNames()
方法。
在 Java 中,我們會透過 getter 和 setter 函式公開類別屬性。Kotlin 可讓我們更清楚區分類別的屬性 (以欄位表示) 和功能 (以函式表示),也就是類別可執行的動作。在本例中,Repository
類別非常簡單,不會執行任何動作,因此只包含欄位。
現在呼叫 formattedUserNames
Kotlin 屬性的 getter 時,會觸發 Java getFormattedUserNames()
函式中觸發的邏輯。
雖然我們沒有與 formattedUserNames
屬性對應的明確欄位,但 Kotlin 會提供名為 field
的自動備份欄位,我們可視需要從自訂 Getter 和 Setter 存取該欄位。
不過,有時我們需要自動支援欄位未提供的額外功能。請參閱下方的範例。
在 Repository
類別中,我們有一個可變動的使用者清單,會透過從 Java 程式碼產生的 getUsers()
函式公開:
fun getUsers(): List<User>? {
return users
}
問題在於,只要傳回 users
,Repository 類別的任何消費者都能修改使用者清單,這並非好主意!讓我們使用支援屬性修正這個問題。
首先,將 users
重新命名為 _users
。現在新增可傳回使用者清單的公開不可變動屬性。我們將其稱為 users
:
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
進行這項變更後,私有 _users
屬性會成為公開 users
屬性的支援屬性。在 Repository
類別外,_users
清單無法修改,因為類別的消費者只能透過 users
存取清單。
完整程式碼:
object Repository {
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
val formattedUserNames: List<String>
get() {
return _users.map { user ->
if (user.lastName != null) {
if (user.firstName != null) {
"${user.firstName} ${user.lastName}"
} else {
user.lastName ?: "Unknown"
}
} else {
user.firstName ?: "Unknown"
}
}
}
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
_users.add(user1)
_users.add(user2)
_users.add(user3)
}
}
目前 Repository
類別知道如何計算 User
物件的格式化使用者名稱。但如果我們想在其他類別中重複使用相同的格式化邏輯,就必須複製並貼上,或是將其移至 User
類別。
Kotlin 允許在任何類別、物件或介面以外宣告函式和屬性。舉例來說,我們用來建立 List
新例項的 mutableListOf()
函式,是直接在標準程式庫的 Collections.kt
中定義。
在 Java 中,每當需要某些公用程式功能時,您很可能會建立 Util
類別,並將該功能宣告為靜態函式。在 Kotlin 中,您可以宣告頂層函式,不必使用類別。不過,Kotlin 也提供建立擴充功能函式的功能。這類函式會擴充特定型別,但會在型別外部宣告。因此,這類使用者與該類型有親和力。
您可以使用瀏覽權限修飾符,限制擴充功能函式和屬性的瀏覽權限。這些限制只會將用法限制在需要擴充功能的類別,不會汙染命名空間。
對於 User
類別,我們可以新增計算格式化名稱的擴充功能函式,也可以在擴充功能屬性中保留格式化名稱。可以在同一檔案中,於 Repository
類別外部新增:
// extension function
fun User.getFormattedName(): String {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
// extension property
val User.userFormattedName: String
get() {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
// usage:
val user = User(...)
val name = user.getFormattedName()
val formattedName = user.userFormattedName
接著,我們就能像使用 User
類別的一部分一樣,使用擴充功能函式和屬性。
由於格式化名稱是使用者的屬性,而非 Repository
類別的功能,因此請使用擴充功能屬性。我們的 Repository
檔案現在看起來像這樣:
val User.formattedName: String
get() {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
object Repository {
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
val formattedUserNames: List<String>
get() {
return _users.map { user -> user.formattedName }
}
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
_users.add(user1)
_users.add(user2)
_users.add(user3)
}
}
Kotlin 標準程式庫會使用擴充功能函式,擴充多個 Java API 的功能;Iterable
和 Collection
上的許多功能都是以擴充功能函式的形式實作。舉例來說,我們在上一個步驟中使用的 map
函式是 Iterable
的擴充功能函式。
在 Repository
類別程式碼中,我們將數個使用者物件新增至 _users
清單。範圍函式可協助您以更慣用的方式進行這些呼叫。
如要只在特定物件的環境中執行程式碼,不必根據名稱存取物件,Kotlin 建立了 5 個範圍函式:let
、apply
、with
、run
和 also
。這些函式簡短有力,全都具有接收器 (this
),可能會有引數 (it
),也可能會傳回值。您可以根據要達成的目標,決定使用哪一種。
以下提供實用速查表,協助你記住這些事項:
由於我們是在 Repository
中設定 _users
物件,因此可以使用 apply
函式,讓程式碼更符合慣例:
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
_users.apply {
// this == _users
add(user1)
add(user2)
add(user3)
}
}
在本程式碼研究室中,我們介紹了將程式碼從 Java 重構為 Kotlin 的基本概念。這項重構作業與開發平台無關,有助於確保您編寫的程式碼符合慣例。
使用 Kotlin 慣用語可簡潔地編寫程式碼。Kotlin 提供許多功能,可讓程式碼更安全、簡潔且易於閱讀。舉例來說,我們甚至可以透過在宣告中直接使用使用者例項建立 _users
清單,藉此最佳化 Repository
類別,並擺脫 init
區塊:
private val users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
我們涵蓋了許多主題,從處理可為空值、單例項、字串和集合,到擴充函式、頂層函式、屬性和範圍函式等主題。我們從兩個 Java 類別改為兩個 Kotlin 類別,現在看起來如下所示:
User.kt
class User(var firstName: String?, var lastName: String?)
Repository.kt
val User.formattedName: String
get() {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
object Repository {
private val _users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
val users: List<User>
get() = _users
val formattedUserNames: List<String>
get() = _users.map { user -> user.formattedName }
}
以下是 Java 功能的簡要說明,以及這些功能對應至 Kotlin 的方式:
Java | Kotlin |
|
|
|
|
|
|
只保留資料的類別 |
|
建構函式中的初始化作業 |
|
| 在 |
單例模式類別 |
|
如要進一步瞭解 Kotlin,以及如何在平台上使用,請參閱下列資源:
- Kotlin Koans
- Kotlin 教學課程
- 使用 Kotlin 開發 Android 應用程式 - 免費課程
- 程式設計人員的 Kotlin 新手上路課程
- 「適合 Java 開發人員的 Kotlin」 - 免費課程 (稽核模式)