Java 與 Kotlin 互操作

本文聊聊 Kotlin 的新特性及其語法糖之Java 與 Kotlin 互操作。


Java 與 Kotlin 互操作


Kotlin 中調用 Java

空安全

Java 聲明的類型在 Kotlin 中會被特別對待並稱為平臺類型。Kotlin 對這種類型的空檢查會放寬,當我們調用平臺類型變量的方法時,Kotlin 不會在編譯時拋出 NullPointerException 異常。

這裡,我們可以使用 Lombok(lombok.NonNull)或 JSR-305(javax.annotation)進行可空性註解,儘量防止拋出 NullPointerException 異常。

屬性

遵循 Java 約定的 getter 和 setter 的方法(名稱以get開頭的無參數方法和以set開頭的單參數方法)在 Kotlin 中表示為屬性。Boolean訪問器方法(其中 getter 的名稱以is開頭而 setter 的名稱以set開頭)會表示為與 getter 方法具有相同名稱的屬性。請注意,如果 Java 類只有一個 setter,它在 Kotlin 中不會作為屬性可見,因為 Kotlin 目前不支持只寫(set-only)屬性。

受檢異常

在 Kotlin 中,所有異常都是非受檢的,因此編譯器不會強迫捕獲其中的任何一個。因此,當你調用一個聲明受檢異常的 Java 方法時,Kotlin 不會強迫做任何事情。

Java 反射

我們可以使用instance::class.java,ClassName::class.java或者instance.javaClass通過java.lang.Class來進入 Java 反射。

SAM 轉換

就像 Java 8 一樣,Kotlin 支持 SAM 轉換。這意味著 Kotlin 函數字面值可以被自動的轉換成只有一個非默認方法的 Java 接口的實現,只要這個方法的參數類型能夠與這個 Kotlin 函數的參數類型相匹配。

val runnable=Runnable{println("This runs in a runnable")}

請注意,SAM 轉換隻適用於接口,而不適用於抽象類,即使這些抽象類也只有一個抽象方法。還要注意,此功能只適用於 Java 互操作;因為 Kotlin 具有合適的函數類型,所以不需要將函數自動轉換為 Kotlin 接口的實現,因此不受支持。

Java 中調用 Kotlin

屬性

如果屬性的名稱以is開頭,則使用不同的名稱映射規則:getter 的名稱與屬性名稱相同,並且 setter 的名稱是通過將is替換為set獲得。例如,對於屬性isOpen,其 getter 會稱做isOpen(),而其 setter 會稱做setOpen()。這一規則適用於任何類型的屬性,並不僅限於Boolean。

常用註解 @JvmName

這裡,會編譯成一個名為org.foo.bar.ExampleKt的 Java 類的靜態方法。我們通過@file:JvmName("DemoUtils")指定類名。

@file:JvmName("DemoUtils")

packagedemo

classFoo

fun bar(){

}

// Java

newdemo.Foo();

demo.DemoUtils.bar();

常用註解 @JvmField

如果需要在 Java 中將 Kotlin 屬性作為字段暴露,那就需要使用@JvmField註解對其標註。該字段將具有與底層屬性相同的可見性。

classC(id:String){

@JvmFieldval ID=id

}

// Java

classJavaClient{

publicStringgetID(C c){

returnc.ID;

}

}

常用註解 @JvmStatic

Kotlin 還可以為命名對象或伴生對象中定義的函數生成靜態方法,如果你將這些函數標註為@JvmStatic的話。如果你使用該註解,編譯器既會在相應對象的類中生成靜態方法,也會在對象自身中生成實例方法。

classC{

companionobject{

@JvmStaticfun foo(){}

fun bar(){}

}

}

現在,foo()在 Java 中是靜態的:

C.foo();// 沒問題

C.Companion.foo();// 保留實例方法

@JvmStatic註解也可以應用於對象或伴生對象的屬性,使其 getter 和 setter 方法在該對象或包含該伴生對象的類中是靜態成員。

常用註解 @JvmOverloads

如果我們寫一個有默認參數值的 Kotlin 函數,在 Java 中只會有一個所有參數都存在的完整參數簽名的方法可見,如果希望向 Java 調用者暴露多個重載,可以使用@JvmOverloads註解。該註解也適用於構造函數、靜態方法等。它不能用於抽象方法,包括在接口中定義的方法。

classFoo@JvmOverloadsconstructor(x:Int,y:Double=0.0){

@JvmOverloadsfun f(a:String,b:Int=0,c:String="abc"){

……

}

}

對於每一個有默認值的參數,都會生成一個額外的重載,這個重載會把這個參數和它右邊的所有參數都移除掉。在上例中,會生成以下代碼:

// 構造函數:

Foo(intx,doubley)

Foo(intx)

// 方法

voidf(Stringa,intb,Stringc){}

voidf(Stringa,intb){}

voidf(Stringa){}

受檢異常

Kotlin 沒有受檢異常。所以,通常 Kotlin 函數的 Java 簽名不會聲明拋出異常。為了解決這個問題,要在 Kotlin 中使用@Throws註解。

@Throws(IOException::class)

fun foo(){

throwIOException()

}

NoArg 和 AllOpen

在 Kotlin 中,data class 默認沒有無參構造方法,並且 data class 默認為 final 類型,不可以被繼承。注意的是,如果我們使用 Spring + Kotlin 的模式,那麼使用 @autowared 就可能遇到這個問題。因此,我們可以添加 NoArg 為標註的類生成無參構造方法。使用 AllOpen 為被標註的類去掉 final,允許被繼承。

org.jetbrains.kotlin

kotlin-maven-noarg

${kotlin.version}

org.jetbrains.kotlin

kotlin-maven-allopen

${kotlin.version}


分享到:


相關文章: