java调用kotlin

皇甫福
2023-12-01

java调用kotlin

在Java代码中,可以轻松的调用Kotlin,无缝对接,然而两者之间有一些不同的地方需要注意。

属性

一个kotlin属性会编译成下面java元素:

  • getter
  • setter
  • private field

例如var firstName: String会被编译成下面这样:

private String firstName;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

如果属性名以is开头,setter方法名会将属性中的is替换为set,getter方法名与属性名相同,例如var isOpen: Boolean被会编译成下面这样:

private boolean isOpen;

public final boolean isOpen() {
  return this.isOpen;
}

public final void setOpen(boolean isOpen) {
  this.isOpen = isOpen;
}

注意这个规则与属性名有关,而不仅仅是与boolean类型有关。

包级别的函数

kotlin中的函数和属性可以直接定义在最外层,不必包含在一个Class中,那么编译后kotlin会自动生成Class类。

// Demo2.kt
package com.morris.kotlin.interop.javacallkotlin

fun method() {}

var name: String = "name"

最后生成的class文件如下:

// Demo2Kt.class
package com.morris.kotlin.interop.javacallkotlin;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(mv = {1, 4, 0}, bv = {1, 0, 3}, k = 2, d1 = {"\000\020\n\000\n\002\020\016\n\002\b\005\n\002\020\002\n\000\032\006\020\006\032\0020\007\"\032\020\000\032\0020\001X\016\006\016\n\000\032\004\b\002\020\003\"\004\b\004\020\005\006\b"}, d2 = {"name", "", "getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "method", "", "untitled"})
public final class Demo2Kt {
  public static final void method() {}
  
  @NotNull
  private static String name = "name";
  
  @NotNull
  public static final String getName() {
    return name;
  }
  
  public static final void setName(@NotNull String <set-?>) {
    Intrinsics.checkNotNullParameter(<set-?>, "<set-?>");
    name = <set-?>;
  }
}

顶层的属性和方法在生成的类中都是static的。

可以使用@JvmName来指定生成的class文件的名称,需要放在文件的第一行。

@file: JvmName("Demo2")
package com.morris.kotlin.interop.javacallkotlin

fun method() {}

var name: String = "name"

一般来说,JvmName()中指定的名字+包名必须是唯一的,但是kotlin支持将多个kotlin文件中的属性和方法打包放到一个class中:

// Util1.kt
@file: JvmName("Util")
@file: JvmMultifileClass
package com.morris.kotlin.interop.javacallkotlin

fun getTime() {}

// Util2.kt
@file: JvmName("Util")
@file: JvmMultifileClass
package com.morris.kotlin.interop.javacallkotlin

fun getDate() {}

最后生成的class文件如下:

package com.morris.kotlin.interop.javacallkotlin;

import kotlin.Metadata;

@Metadata(mv = {1, 4, 0}, bv = {1, 0, 3}, k = 4, d1 = {"com/morris/kotlin/interop/javacallkotlin/Util__Util1Kt", "com/morris/kotlin/interop/javacallkotlin/Util__Util2Kt"})
public final class Util {
  public static final void getDate() {
    Util__Util2Kt.getDate();
  }
  
  public static final void getTime() {
    Util__Util1Kt.getTime();
  }
}

实例字段

可以使用@JvmField将属性作为一个public的字段暴露给java,而不是通过setter和getter访问。

class User(id: String) {
    @JvmField val ID = id
}

这样生成的class会有个public的字段,而没有setter和getter方法。

public final class User {
  @JvmField
  @NotNull
  public final String ID;
  
  public User(@NotNull String id) {
    this.ID = id;
  }
}

静态字段

使用@JvmField注解一个伴生对象的属性,那么这个属性就会成为外部类的一个静态属性:

class Key(val value: Int) {
    companion object {
        @JvmField
        val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
    }
}

在java中如下使用:

// Java
Key.COMPARATOR.compare(key1, key2);
// public static final field in Key class

对象声明或者伴生对象中的lateinit修饰的属性会成为类的静态属性:

object Singleton {
    lateinit var provider: Provider
}

在java中如下使用:

// Java
Singleton.provider = new Provider();
// public static non-final field in Singleton class

const修饰的属性也会成为静态属性:

// file example.kt

object Obj {
    const val CONST = 1
}

class C {
    companion object {
        const val VERSION = 9
    }
}

const val MAX = 239

在java中如下使用:

int const = Obj.CONST;
int max = ExampleKt.MAX;
int version = C.VERSION;

静态方法

跟属性一样,顶级的函数是静态的方法。

kotlin也支持使用@JvmStatic注解为定义在命名对象或派生对象中的方法生成静态方法:

class C {
    companion object {
        @JvmStatic fun callStatic() {}
        fun callNonStatic() {}
    }
}

在java中如下使用:

C.callStatic(); // works fine
C.callNonStatic(); // error: not a static method
C.Companion.callStatic(); // instance method remains
C.Companion.callNonStatic(); // the only way it works

重载方法的生成

kotlin支持默认参数,而java中没有这个特性,可以使用@JvmOverloads注解暴露多个方法供java调用:

class Circle @JvmOverloads constructor(centerX: Int, centerY: Int, radius: Double = 1.0) {
    @JvmOverloads fun draw(label: String, lineWidth: Int = 1, color: String = "red") { /*...*/ }
}

在java中如下使用:

// Constructors:
Circle(int centerX, int centerY, double radius)
Circle(int centerX, int centerY)

// Methods
void draw(String label, int lineWidth, String color) { }
void draw(String label, int lineWidth) { }
void draw(String label) { }
 类似资料: