Kotlin-45.Java調用kotlin之三(Call Kotlin from Java)

官方文檔: http://kotlinlang.org/docs/reference/java-to-kotlin-interop.html

8.@JvmName解決java方法簽名相同(Handling signature clashes)

最突出的例子是由於類型擦除(type erasure)引發:
    // 類型擦除: 無法區分List<String>和List<Int>
    fun List<String>.filterValid(): List<String>
    fun List<Int>.filterValid(): List<Int>
    這兩個函數在java中不能同時定義,因爲它們的JVM簽名相同: filterValid(Ljava/util/List;)Ljava/util/List

在Kotlin中用相同名稱,需要用@JvmName標註其中的一個(或兩個),並指定不同名稱:
    fun List<String>.filterValid(): List<String>

    @JvmName("filterValidInt")
    fun List<Int>.filterValid(): List<Int>

    在Kotlin中,可以用相同名稱filterValid()訪問;
    在Java中,需要分別用filterValid()和filterValidInt()訪問

同樣註解@JvmName也適用於屬性x和函數getX():
    val x: Int
        @JvmName("getX_prop")
        get() = 15

    fun getX() = 10

    在Java中,需要分別用getX_prop()和getX()訪問

9.Java方法重載(Overloads Generation)

如果Kotlin函數的參數有默認值並且使用@JvmOverloads註解,
那麼在Java中多個重載方法, 示例如下:
    @JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") {
    }

    // kotlin函數的每一個有默認值的參數,都會重載生成一個額外java方法:    
    void f(String a, int b, String c) {            
    }
    void f(String a, int b) {            
    }
    void f(String a) {            
    }

該註解也適用於構造函數和靜態方法, 但不能用於抽象方法(包括接口的方法)

對於次構造函數(Secondary Constructors), 如果所有參數都有默認值,
那麼會生成一個公有public的無參構造函數(沒有@JvmOverloads註解也會生效)!

10.受檢異常(Checked Exception)

從前幾章《kotlin-33.異常(Exception)》可知,Kotlin沒有受檢異常!
所以Kotlin函數簽名不會聲明拋出異常(throws Exception),例如:    
    // kotlin (example.kt), 拋出異常
    package demo
    fun foo() {
        throw IOException()
    }

    // kotlin編譯生成的Java方法
    public void foo() { // 錯誤: foo()沒有聲明throws IOException
        throw IOException()
    }

因爲由kotlin函數生成的Java方法foo()沒有聲明throws IOException, 所以Java編譯器報錯!
爲了解決這個問題,需要在Kotlin中使用@Throws註解(相當於在Java中聲明throws IOException)
    // kotlin
    @Throws(IOException::class)
    fun foo() {
        throw IOException()
    }

    // kotlin編譯生成的Java方法
    public void foo() throws IOException {
        throw IOException()
    }

11.型變泛型(Variant generics)

當Kotlin類使用聲明處型變(declaration-site variance)時,在Java中有兩種用法!
示例:
    // kotlin
    class Box<out T>(val value: T)
    interface Base
    class Derived : Base

    fun boxDerived(value: Derived): Box<Derived>{            
    }
    fun unboxBase(box: Box<Base>): Base {            
    }
    unboxBase(boxDerived("s")) // 正確: 在Java中Box<Base>泛型是不型變的!

    // 將上述kotlin函數轉換成Java方法       
    Box<Derived> boxDerived(Derived value) {            
    }
    Base unboxBase(Box<Base> box) {            
    }
    unboxBase(boxDerived("s")) // 錯誤: 因爲在Java中Box<Base>泛型是不型變的!

    //使用Java通配符類型<? extends Base>模擬kotlin聲明處型變
    Base unboxBase(Box<? extends Base> box) {            
    }
    unboxBase(boxDerived("s")) // 正確

此外, 對於協變定義的Box(在kotlin中Box<in T>), 在java中使用Box<? extends Super>
注意:當參數類型是final時,通配符? extends沒有意義,
        例如在Box<String>中的String類是final,沒有子類(不能被繼承extends)

如果在默認沒有通配符處要求java泛型通配符, 可以使用@JvmWildcard註解:
    // kotlin函數
    fun boxDerived(value: Derived): Box<@JvmWildcard Derived> {            
    }

    // 轉換成java方法 
    Box<? extends Derived> boxDerived(Derived value) {        
    }

相反,如果不需要泛型通配符,可以使用@JvmSuppressWildcards註解;
@JvmSuppressWildcards不僅可用於單個類型參數,還可用於整個聲明(如函數或類),從而抑制其中的所有通配符!
    // kotlin函數
    fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base {            
    }

    // 轉換成java方法 
    Base unboxBase(Box<Base> box) {            
    }

12.Nothing類型翻譯(Translation of type Nothing)

類型Nothing是Kotlin特有的,在Java中沒有對應類型!
每個Java引用類型(包括java.lang.Void)都接受null, 但是kotlin的Nothing不行!
Nothing類型不能在Java世界中準確表示,所以Nothing類型在java中會消失(原始類型raw type):
    // kotlin的Nothing類型
    fun emptyList(): List<Nothing> = listOf()

    // 翻譯成轉換成java方法, List<Nothing>變成原始類型List, Nothing類型消失了
    List emptyList() {            
    }

簡書:http://www.jianshu.com/p/acdd8ba0830b
CSDN博客: http://blog.csdn.net/qq_32115439/article/details/75452680
GitHub博客:http://lioil.win/2017/07/19/Kotlin-kotlinInJava3.html
Coding博客:http://c.lioil.win/2017/07/19/Kotlin-kotlinInJava3.html

發佈了156 篇原創文章 · 獲贊 38 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章