在Java代码中,可以轻松的调用Kotlin,无缝对接,然而两者之间有一些不同的地方需要注意。
一个kotlin属性会编译成下面java元素:
例如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) { }