文章目录

  1. 1. Kotlin 中调用 Java
    1. 1.1. 空安全
    2. 1.2. 属性
    3. 1.3. 受检异常
    4. 1.4. Java 反射
    5. 1.5. SAM 转换
  2. 2. Java 中调用 Kotlin
    1. 2.1. 属性
    2. 2.2. 常用注解 @JvmName
    3. 2.3. 常用注解 @JvmField
    4. 2.4. 常用注解 @JvmStatic
    5. 2.5. 常用注解 @JvmOverloads
    6. 2.6. 受检异常
    7. 2.7. NoArg 和 AllOpen


微信公众号

转载自 https://www.kotlincn.net/,并添加部分自己的经验与理解。

Kotlin 中调用 Java

空安全

Java 声明的类型在 Kotlin 中会被特别对待并称为平台类型。Kotlin 对这种类型的空检查会放宽,当我们调用平台类型变量的方法时,Kotlin 不会在编译时抛出 NullPointerException 异常。

这里,我们可以使用 Lombok(lombok.NonNull)或 JSR-305(javax.annotation)进行可空性注解,尽量防止抛出 NullPointerException 异常。

属性

遵循 Java 约定的 getter 和 setter 的方法(名称以 get 开头的无参数方法和以 set 开头的单参数方法)在 Kotlin 中表示为属性。Boolean 访问器方法(其中 getter 的名称以 is 开头而 setter 的名称以 set 开头)会表示为与 getter 方法具有相同名称的属性。请注意,如果 Java 类只有一个 setter,它在 Kotlin 中不会作为属性可见,因为 Kotlin 目前不支持只写(set-only)属性。

受检异常

在 Kotlin 中,所有异常都是非受检的,因此编译器不会强迫捕获其中的任何一个。因此,当你调用一个声明受检异常的 Java 方法时,Kotlin 不会强迫做任何事情。

Java 反射

我们可以使用 instance::class.java,
ClassName::class.java 或者 instance.javaClass 通过 java.lang.Class 来进入 Java 反射。

SAM 转换

就像 Java 8 一样,Kotlin 支持 SAM 转换。这意味着 Kotlin 函数字面值可以被自动的转换成只有一个非默认方法的 Java 接口的实现,只要这个方法的参数类型能够与这个 Kotlin 函数的参数类型相匹配。

val runnable = Runnable { println("This runs in a runnable") }

请注意,SAM 转换只适用于接口,而不适用于抽象类,即使这些抽象类也只有一个抽象方法。还要注意,此功能只适用于 Java 互操作;因为 Kotlin 具有合适的函数类型,所以不需要将函数自动转换为 Kotlin 接口的实现,因此不受支持。

Java 中调用 Kotlin

属性

如果属性的名称以 is 开头,则使用不同的名称映射规则:getter 的名称与属性名称相同,并且 setter 的名称是通过将 is 替换为 set 获得。例如,对于属性 isOpen,其 getter 会称做 isOpen(),而其 setter 会称做 setOpen()
这一规则适用于任何类型的属性,并不仅限于 Boolean

常用注解 @JvmName

这里,会编译成一个名为 org.foo.bar.ExampleKt 的 Java 类的静态方法。我们通过 @file:JvmName("DemoUtils") 指定类名。

@file:JvmName("DemoUtils")

package demo

class Foo

fun bar() {
}
// Java
new demo.Foo();
demo.DemoUtils.bar();

常用注解 @JvmField

如果需要在 Java 中将 Kotlin 属性作为字段暴露,那就需要使用 @JvmField 注解对其标注。该字段将具有与底层属性相同的可见性。

class C(id: String) {
    @JvmField val ID = id
}
// Java
class JavaClient {
    public String getID(C c) {
        return c.ID;
    }
}

常用注解 @JvmStatic

Kotlin 还可以为命名对象或伴生对象中定义的函数生成静态方法,如果你将这些函数标注为 @JvmStatic 的话。如果你使用该注解,编译器既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法。

class C {
    companion object {
        @JvmStatic fun foo() {}
        fun bar() {}
    }
}

现在,foo() 在 Java 中是静态的:

C.foo(); // 没问题
C.Companion.foo(); // 保留实例方法

@JvmStatic 注解也可以应用于对象或伴生对象的属性,使其 getter 和 setter 方法在该对象或包含该伴生对象的类中是静态成员。

常用注解 @JvmOverloads

如果我们写一个有默认参数值的 Kotlin 函数,在 Java 中只会有一个所有参数都存在的完整参数签名的方法可见,如果希望向 Java 调用者暴露多个重载,可以使用@JvmOverloads 注解。该注解也适用于构造函数、静态方法等。它不能用于抽象方法,包括在接口中定义的方法。

class Foo @JvmOverloads constructor(x: Int, y: Double = 0.0) {
    @JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") {
        ……
    }
}

对于每一个有默认值的参数,都会生成一个额外的重载,这个重载会把这个参数和它右边的所有参数都移除掉。在上例中,会生成以下代码:

// 构造函数:
Foo(int x, double y)
Foo(int x)

// 方法
void f(String a, int b, String c) { }
void f(String a, int b) { }
void f(String a) { }

受检异常

Kotlin 没有受检异常。所以,通常 Kotlin 函数的 Java 签名不会声明抛出异常。为了解决这个问题,要在 Kotlin 中使用 @Throws 注解。

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

NoArg 和 AllOpen

在 Kotlin 中,data class 默认没有无参构造方法,并且 data class 默认为 final 类型,不可以被继承。注意的是,如果我们使用 Spring + Kotlin 的模式,那么使用 @autowared 就可能遇到这个问题。因此,我们可以添加 NoArg 为标注的类生成无参构造方法。使用 AllOpen 为被标注的类去掉 final,允许被继承。

<dependencies>
    <dependency>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-maven-noarg</artifactId>
        <version>${kotlin.version}</version>
    </dependency>
    <dependency>
         <groupId>org.jetbrains.kotlin</groupId>
         <artifactId>kotlin-maven-allopen</artifactId>
         <version>${kotlin.version}</version>
     </dependency>
</dependencies>

文章目录

  1. 1. Kotlin 中调用 Java
    1. 1.1. 空安全
    2. 1.2. 属性
    3. 1.3. 受检异常
    4. 1.4. Java 反射
    5. 1.5. SAM 转换
  2. 2. Java 中调用 Kotlin
    1. 2.1. 属性
    2. 2.2. 常用注解 @JvmName
    3. 2.3. 常用注解 @JvmField
    4. 2.4. 常用注解 @JvmStatic
    5. 2.5. 常用注解 @JvmOverloads
    6. 2.6. 受检异常
    7. 2.7. NoArg 和 AllOpen