1. 程式人生 > >Kotlin開發語言學習(3)Kotlin與Java混編

Kotlin開發語言學習(3)Kotlin與Java混編

雖然 Kotlin 的開發很方便,但當你與他人協作時,總會碰到 Java 與 Kotlin 程式碼共存的程式碼專案。
本章就教你如何優雅的實現 Kotlin 與 Java 混合程式設計。

3.1 直接轉換

3.1.1 將 Java 轉換為 Kotlin

如果你之前使用 Java 語言而沒有 Kotlin 開發經驗,不用擔心,Intellij IDEA 會幫你一鍵轉換,將 Java 程式碼轉換成 Kotlin 程式碼(但是反過來就不行了)。
在 Mac 上,系統預設的快捷鍵為control+shift+command+K,這個組合鍵實在有點反人類,建議你自定義一個你覺得舒服的快捷鍵。
快捷鍵可以通過你的編譯器 keymap

 中修改:command+, -> 搜尋keymap -> 右側搜尋kotlin,可以檢視到Convert Java File to Kotlin File項。

圖3-1-1

3.1.2 注意 Class 呼叫

在 Java 或 Android 開發中,經常會直接呼叫一個類的 Class 檔案。但是當你用上文介紹的轉換方法去轉換 XXX.class這樣的程式碼時,是無法直接轉換的(也許未來會修復這個問題,但目前你扔需要手動修改)。在 M13 之前,Java 中的XXX.class對應 Kotlin 程式碼中的JavaClass<XXX>,而 M13 之後寫法已被改為XXX::class.java

3.1.3 Android proguard 的坑

注:我們團隊遇到過這樣的一個坑,在 Android 開發的時候,如下程式碼會在混淆以後,發生異常

var str = some?.s?.d ?: ""

這段程式碼在正常debug模式編譯執行完全正常,但是一旦執行混淆,就會發生所在函式被移除的現象。
但是如果改寫為以下寫法就能正常執行:

var str = some?.s?.d ?: String()

猜想應該是 proguard 不知道如何處理這段程式碼,無法識別出最後兩個引號是一個String,最後直接將整個函式移除掉了。

同樣的程式碼還有:

var list = some?.data?.list
:mutableListof()

但是如下程式碼即使混淆後也是可以完全正常執行的

var s = some?.s ?: ""  
var s = some.d ?: ""
var list = some?.data?.list:klist  
var data = some?.data ?: return

3.1.4 開發 Android library 的建議

如果你是開發 Android library 程式,建議你不要使用 Kotlin 程式碼。因為作為 library,如果使用它的工程是純 Java 完成的,引入後會額外增大 200k 左右大小,同時它有可能會造成某些情況下編譯異常。

3.2 在 Kotlin 中呼叫 Java 程式碼

3.2.1 返回 void 的方法

如果一個 Java 方法返回 void,對應的在 Kotlin 程式碼中它將返回 Unit。關於 Unit,本書將在 第五章函式部分著重講解。 
現在你只需要知道在Java 中返回為 void 的函式,在 Kotlin 中可以省略這個返回型別。

3.2.2 與 Kotlin 關鍵字衝突的處理

Java 有 static 關鍵字,在 Kotlin 中沒有這個關鍵字,你需要使用@JvmStatic替代這個關鍵字。
同樣,在 Kotlin 中也有很多的關鍵字是 Java 中是沒有的。例如 in,is,data等。如果 Java 中使用了這些關鍵字,需要加上反引號(`)轉義來避免衝突。例如

// Java 程式碼中有個方法叫 is()
public void is(){
	//...
}

// 轉換為 Kotlin 程式碼需要加反引號轉義
fun `is`() {
   //...
}

3.3 在 Java 中呼叫 Kotlin 程式碼

3.3.1 static 方法

上文已經提到過,在 Kotlin 中沒有 static關鍵字,那麼如果在 Java 程式碼中想要通過類名呼叫一個 Kotlin 類的方法,你需要給這個方法加入@JvmStatic註解。否則你必須通過物件呼叫這個方法。

StringUtils.isEmpty("hello");  
StringUtils.INSTANCE.isEmpty2("hello");

object StringUtils {
    @JvmStatic fun isEmpty(str: String): Boolean {
        return "" == str
    }

    fun isEmpty2(str: String): Boolean {
        return "" == str
    }
}

如果你閱讀 Kotlin 程式碼,應該經常看到這樣一種寫法。

class StringUtils {
    companion object {
       fun isEmpty(str: String): Boolean {
	        return "" == str
	    }
    }
}

companion object表示外部類的一個伴生物件,你可以把他理解為外部類自動建立了一個物件作為自己的field
與上面的類似,Java 在呼叫時,可以這樣寫:StringUtils.Companion. isEmpty();
關於伴生物件,我們將在下一章 類與物件 詳細講解。

3.3.2 包級別函式

與 Java 不同,Kotlin 允許函式獨立存在,而不必依賴於某個類,這類函式我們稱之為包級別函式(Package-Level Functions)。
為了相容 Java,Kotlin 預設會將所有的包級別函式放在一個自動生成的叫ExampleKt的類中, 在 Java 中想要呼叫包級別函式時,需要通過這個類來呼叫。 
當然,也是可以自定義的,你只需要通過註解@file:JvmName("Example")即可將當前檔案中的所有包級別函式放到一個自動生成的名為 Example 的類中。

3.3.3 空安全性

在 Java 中,如果你呼叫的 kotlin 方法引數聲明瞭非空型別,如果你在 Java 程式碼中傳入一個空值,將在執行時丟擲NullPointerException。其內部原因在於 Kotlin 為每個非空型別加了斷言,如果傳入空值則會立刻丟擲異常。 
同樣,如果你使用 null 物件去呼叫一個 kotlin 方法,將會立刻丟擲NullPointerException(就算是呼叫普通 java 方法也是一樣會丟擲 NullPointerException )