天天看點

kotlin 和java 混編

如果你之前使用 Java 語言而沒有 Kotlin 開發經驗,不用擔心,Intellij IDEA 會幫你一鍵轉換,将 Java 代碼轉換成 Kotlin 代碼(但是反過來就不行了)。

在 Mac 上,系統預設的快捷鍵為

control+shift+command+K

,這個組合鍵實在有點反人類,建議你自定義一個你覺得舒服的快捷鍵。

快捷鍵可以通過你的編譯器 keymap 中修改:

command+,

 -> 搜尋

keymap

 -> 右側搜尋

kotlin

,可以檢視到

Convert Java File to Kotlin File

項。

kotlin 和java 混編

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 開發的時候,如下代碼會在混淆以後,發生異常

這段代碼在正常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

注解(這個注解隻在 jvm 平台有用)。否則你必須通過對象調用這個方法。

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();

(1.1以後可以省略中間的 Companion,寫作 StringUtils.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 )