如何在 Java 和 Kotlin 之間進行互操做

本文首發於微信公衆號「Android開發之旅」,歡迎關注 ,獲取更多技術乾貨java

前言

目前kotlin是谷歌首推的開發Android的語言,但因爲歷史緣由,咱們絕大部分項目依舊仍是以Java爲主的,也就是說存在Java和Kotlin兩種語言同時開發的狀況。bash

有人會說把老項目所有翻譯成Kotlin,的確能夠怎麼作,可是成本仍是挺大的。咱們只能一點一點慢慢的向kotlin語言遷移。微信

那麼在遷移的過程當中就避免不了Java和Kotlin相互調用的狀況。即Kotlin調用Java或者Java調用Kotlin。下面咱們就來具體看下二者之間相互操做的一些解決方案。函數

kotlin調用java

可空性(Nullability)

Java默認有數值可空性而kotlin沒有,因此在調用Java的方法的時候不知道會不會收到空值。 因此咱們在Kotlin中調用Java的時候須要添加 ?或者 !來告訴Kotlin有可能出現空值。工具

好比這裏有一個Java方法,接受一組字符串後返回一組作字符串:ui

public Set<String> toSet(Collection<String> elements){
    //TODO
}
複製代碼

那麼Kotlin在調用的時候是不能肯定輸入和輸出是否可爲空的。就須要使用?或者 !來​輔助判斷。​this

爲了方便Kotlin調用,咱們一般使用 @NotNull 註解來標識Java代碼的非原始參數、字段、返回值。spa

@NotNull
Set<@NotNull String> toSet(@NotNull Collection<@NotNull String> elements){
    //TODO
}
複製代碼

這個Kotlin在調用的時候就明確知道不能爲空,這裏咱們使用的是jetBrain的 @NotNull註解,固然還有其餘選擇,以下圖:翻譯

這裏仍是推薦使用JetBrain或者Android的註解。code

前綴屬性:(getter、setter)

若是是使用Java bean,那麼咱們在Kotlin中調用就沒有什麼問題。

若是你的空參數方法是以get開頭的,那麼Kotlin就知道這是getter,就能夠經過屬性名來訪問它。 相同的若是是由set開頭的單一參數方法,那麼Kotlin就知道這是setter,就經過屬性名直接賦值。 固然is的工做原理也是和它們相似的。

咱們定義一個Java bean:

class User {
    
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
複製代碼

Kotlin中訪問

val user = User()
user.name = "四爺" //賦值
val age = user.age //獲取age字段值
複製代碼

關鍵字(keywords)

kotlin中有不少系統定義的關鍵字如 fun is in objects、typeof、val、var、when、typealias等。

這些關鍵字在java是能夠被使用的,可是在kotlin倒是不行的。

函數或者參數使用了這些關鍵字,那麼kotlin在調用的時候會出現一些問題,好比Java中定義了一個方法名叫 is 的方法。那麼在Kotlin中直接調用就會報錯。

那麼最簡單的方法就是重命名Java方法,但若是調用的是三方庫的方法,就很難去重命名了。 因此咱們另外一種解決方式是在Kotlin調用java方法的時候加上 `` 反引號來使用。

Utils.`is`()
複製代碼

可是咱們若是能重命名仍是重命名,以防止代碼出現太多的符號。

避免在任何擴展方法和擴展屬性上使用Any

運算符重載(operator Overloading)

在Java不存在運算符重載,而kotlin有。好比:

a+b => a.plus(b)
複製代碼

在kotlin中將運算符 + 翻譯爲了方法 plus。

若是在Java中使用了一樣的方法名稱,好比 加(plus)、 減(minus)或者其餘運算符名稱,那麼請務必確保他們與運算符兼容,避免意外調用他們。

Java調用Kotlin

JvmName & JvmMultifileClass

當咱們在遷移的時候會將Java的工具類翻譯爲Kotlin拓展函數或者頂層函數。可是這樣處理以後,在Java文件中是沒法直接調用的,此時咱們須要加註解 @file:JvmName(「文件名稱」):

Ext.kt文件

@file:JvmName("ExtUtils")
package com.demo.javaAndKotlin

fun a(): String {
    ...
}

fun b(): String {
    ...
}
複製代碼

這裏咱們將名稱命名爲ExtUtils。此外,咱們可能還有其餘的頂層函數或者擴展函數。按照上面這種方式咱們也能夠指定一個其餘的名稱,可是若是咱們也想使用ExtUtils這個名稱的時候會報錯:

Duplicate JVM class name 
複製代碼

此時咱們須要在不一樣的文件中加入新的註解 @file:JvmMultifileClass 。意思是將全部的文件合併到一個新的名稱爲ExtUtils文件中。

ExtOther.kt文件

@file:JvmMultifileClass
@file:JvmName("ExtUtils")
package com.demo.javaAndKotlin

fun c(str: Any): String {
   ...
}
複製代碼

咱們在Ext.kt文件中也加入@file:JvmMultifileClass註解,咱們就能夠在Java文件中直接使用ExtUtils來調用 a(),b(),c()方法了。

JvmField

在 kotlin中咱們使用的數據類即 data class 是不須要指定getter和setter的,能夠直接經過字段名來訪問它們。可是若是是在Java文件中調用data class依舊是須要使用getter和setter方法進行調用的。這裏咱們是能夠修改他們的,那就是使用 @JvmField 註解,經過註解,能夠直接將字段暴露出去進行訪問。​

data class Person(

    @JvmField var name: String,
    @JvmField var age: Int
)

//java中調用
Person person = new Person("",1);
person.name = "";
person.age = 10;
複製代碼

可是也有例外就是lateinit修飾的字段會自動暴露,無需指定@JvmField註解。還有const修飾的字段也是同樣會自動暴露。

另外,若是咱們想在Java中調用setName的時候修改這個屬性名稱不叫setName,這裏咱們須要使用@set:JvmName 註解。同理修改getName使用@get:JvmName 。須要注意的是,指定了@set:JvmName或者@get:JvmName註解後不須要在指定@JvmField了。

data class Person(

    @set:JvmName("changeName")
    var name: String,
    @JvmField var age: Int,
    @get:JvmName("likesPink")
    var likesPink: Boolean
){
    lateinit var address:String
}
複製代碼

JvmStatic

當咱們將Java文件的靜態方法遷移到Kotlin中時,咱們會將其放在 companion object中,可是這樣處理以後在Java文件中沒法直接調用,得經過companion對象實例方法來調用。

class MyService {
    internal fun doWork() {
        ...
    }

    companion object {
        fun schedule(context: Context) {
            ...
        }
    }
}

//在Java中調用
MyService.Companion.schedule(this);

複製代碼

幸運的是Kotlin提供了 @JvmStatic 註解。他會讓Kotlin在編譯器完成類封裝後生成一個靜態方法。

class MyService {
    internal fun doWork() {
        ...
    }

    companion object {
   		@JvmStatic
        fun schedule(context: Context) {
            ...
        }
    }
}

//在Java中調用
MyService.schedule(this);

複製代碼

JvmOverloads

在Kotlin中咱們能夠給函數的參數設置默認值,即默認參數。可是這個功能在Java中是沒有的。若是不作任何處理,那麼在Java中調用函數的時候,就必須每一個參數都要傳入。那麼咱們設置的默認參數就沒有任何意義了。

因此,Kotlin給咱們提供了 @JvmOverloads註解,使用這個註解後,會讓Kotlin編譯器按照從左向右的順序依次爲每個可選參數生成重載。

@JvmOverloads
fun Bitmap.resize(width: Int, height: Int = 200) {

}

//java調用
ExtUtils.resize(bitmap,100);
複製代碼

這裏咱們在Kotlin中很容易就理解了Bitmap.resize方法的含義,可是ExtUtils.resize這樣調用的時候,方法名不夠明確。因此咱們能夠使用@JvmName註解來指定名稱。

@JvmName("resizeBitmap")
@JvmOverloads
fun Bitmap.resize(width: Int, height: Int = 200) {

}
//java調用
ExtUtils.resizeBitmap(bitmap,100);
複製代碼

掃描下方二維碼關注公衆號,獲取更多技術乾貨。

相關文章
相關標籤/搜索