如何在 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);
复制代码

扫描下方二维码关注公众号,获取更多技术干货。

相关文章
相关标签/搜索