Kotlin互操作性与可空性

本文最后更新于:2022年11月30日 晚上

互操作性与可空性

互操作性与可空性

Java世界里所有对象都可能是null,当一个Kotlin函数返回String类型值,你不能想当然地认为它的返回值就能符合Kotlin关于空值的规定。

新建Jhava类

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class Jhava {
    @NotNull
    public String utterGreeting() {
        return "HELLO";
    }

    @Nullable
    public String determineFriendshipLevel() {
        return null;
    }
}

类型映射

代码运行时,所有的映射类型都会重新映射回对应的Java类型。

val hitPoint = adversary.hitPoints
println(hitPoint.dec()) // 999
println(hitPoint.javaClass) // int

属性、异常与互操作

属性访问

public int getHitPoints() {
    System.out.println("-------getHitPoints-------");
    return hitPoints;
}

public void setHitPoints(int hitPoints) {
    System.out.println("-------setHitPoints-------");
    this.hitPoints = hitPoints;
}

不需要调用相关getter,setter方法,你可以使用对象名.字段名来获取或者设置一个Java类的字段值。实际上还是暗中调用了getter,setter方法。

adversary.hitPoints = 888
println(adversary.hitPoints)
// -------setHitPoints-------
// -------getHitPoints-------
// 888

Java调用Kotlin

@file:JvmName(“name”)

Kotlin在类Hreo中定义方法

fun makeProclamation() = "Greetings,beast!"

Java在主方法中调用

public static void main(String[] args) {
    System.out.println(HeroKt.makeProclamation());
}

修改编译后Kotlin文件的名字,在文件首行加上

@file:JvmName("Hero")

Java在调用就可以使用新名字

public static void main(String[] args) {
    System.out.println(Hero.makeProclamation());
}
@JvmField

正常使用Java获取或者修改Kotlin类的属性时,Kotlin会提供自动生成的getter,setter方法。

Kotlin类

class SpellsBook {
    val spells = listOf("Magic Wu Wu", "Lay on Hans")
}

Java在主方法中使用

SpellsBook spellsBook = new SpellsBook();
spellsBook.getSpells();

为Kotlin类的属性添加@JvmField注解,你就可以使用对象名.字段名来获取或者设置一个Java字段值

class SpellsBook {
    @JvmField
    val spells = listOf("Magic Wu Wu", "Lay on Hans")
}
SpellsBook spellsBook = new SpellsBook();
List<String> list =  spellsBook.spells;
for (String s : list) {
    System.out.println(s);
}
// Magic Wu Wu
// Lay on Hans
@JvmOverloads

@JvmOverloads注解协助产生Kotlin函数的重载版本。设计一个可能会暴露给Java用户使用的API时,记得使用@JvmOverloads注解,这样,无论你是Kotlin开发者还是Java开发者,都会对这个API的可靠性感到满意。

fun handoverFood(leftHand: String = "berries", rightHand: String = "beef") {
    println("Emmmm... you hand over some delicious $leftHand and $rightHand")
}

在kt中调用没有参数个数问题,但是在Java中如果只填入一个参数,另一个想使用默认值,就会报错,为了解决这个问题,使用@JvmOverloads注解。

@JvmOverloads
fun handoverFood(leftHand: String = "berries", rightHand: String = "beef") {
    println("Emmmm... you hand over some delicious $leftHand and $rightHand")
}

在Java中调用不报错了

Hero.handoverFood("apple");
伴生对象和@JvmStatic

@JvmField注解还能用来以静态方式提供伴生对象里定义的值。
@JvmStatic注解的作用类似于@JvmField,允许你直接调用伴生对象里的函数。

Kotlin类定义伴生对象

class SpellsBook {
    @JvmField
    val spells = listOf("Magic Wu Wu", "Lay on Hans")
    companion object{
        @JvmField
        val MAX_SPELLS_COUNT = 10
        @JvmStatic
        fun getSpellsBookGreeting() = println("I am the greatest Magician")
    }
}

在Java中调用

System.out.println(SpellsBook.MAX_SPELLS_COUNT); // 10
SpellsBook.getSpellsBookGreeting(); // I am the greatest Magician

@Throws

抛出一个需要检查的指定异常,Java和Kotlin有关异常检查的差异让@Throws注解给解决掉了,在编写供Java开发者调用的Kotlin API时,要考虑使用@Throws注解,这样,用户就知道怎么正确处理任何异常了。

在Java抛出的异常Kotlin不会强制提示在编译时处理,反之亦然。

(1)在Java类中抛出异常
public void extendHandInFriendship() throws IOException{
    throw new IOException();
}

Kotlin中调用会抛出异常的类并没有报错,但是如果运行还是会报错
adversary.extendHandInFriendship() // 没有报错

(2)在Kotlin中抛出异常

fun acceptApology() {
    throw IOException()
}

Java中并没有强制编译时处理

Hero.acceptApology();

尝试用try catch处理,却报错

try {
    Hero.acceptApology();
}catch (IOException e){ // 报错
    System.out.println("Caught!");
}

反编译成Java发现,原来Kotlin在编译时把IOException强制转换成了Throwable类型

public static final void acceptApology() {
      throw (Throwable)(new IOException());
   }

在try catch将IOException改为Throwable发现报错消失

try {
    Hero.acceptApology();
}catch (Throwable e){ // 报错消失
    System.out.println("Caught!");
}

但是如果我们要正常处理IOException,而且强制编译时处理,就要给kt里抛出错误的方法加上@Throws注释。

@Throws(IOException::class)
fun acceptApology() {
    throw IOException()
}

此时java里try-catch不报错

函数类型操作

函数类型和匿名函数能提供高效的语法用于组件间的交互,是Kotlin编程语言里比较新颖的特性。他们简洁的语法因->操作符而实现,但Java8之前的JDK版本并并不支持lambda表达式。在Java里,Kotlin函数类型使用FunctionN这样的名字的接口来表示的,FunctionN中的N代表值参数目。这样的Function接口由23个,从Function0到Function22,每一个FunctionN都包含一个invoke函数,专用于调用函数类型函数,所以,在何时候需要调一个函数类型,都用它调用invoke。

在kt类中添加一个translator的函数类型

// 添加一个translator的函数类型,接收一个字符串
// 将其改为小写字母,再大写第一个字符,最后打印结果。
val translator = { utterance: String ->
    println(utterance.lowercase().capitalize())
}

在Java主方法中调用

Function1<String, Unit> translator = Hero.getTranslator();
translator.invoke("HELLO"); // Hello

Kotlin互操作性与可空性
http://yorick.love/2022/09/15/Kotlin/Kotlin互操作性与可空性/
作者
Yorick
发布于
2022年9月15日
许可协议