ZetCode

Kotlin external 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的 external 关键字实现了与原生代码的互操作性。 它标记了在 Kotlin 之外实现的声明。 本教程通过实际例子深入探讨了 external 关键字。

基本定义

Kotlin 中的 external 关键字表示声明是在特定于平台的代码中实现的。 它用于 JNI (Java Native Interface) 和 JavaScript 互操作性。 实际的实现必须在外部提供。

基本 external 函数

external 最简单的用法是声明一个在原生代码中实现的函数。 函数体被省略,因为它是在外部提供的。

NativeFunction.kt
package com.zetcode

external fun helloFromNative()

fun main() {
    helloFromNative()
}

在这里,我们声明一个没有实现的 external 函数。 实际的原生代码将用 C/C++ 编写,并通过 JNI 链接。 Kotlin 代码可以像调用任何其他函数一样调用它。

使用 JNI 的 external

在使用 JNI 时,external 标记在 C/C++ 中实现的函数。 函数名称必须遵循 JNI 命名约定才能正确链接。

JNIFunction.kt
package com.zetcode

external fun nativeAdd(a: Int, b: Int): Int

fun main() {
    System.loadLibrary("nativeLib")
    val sum = nativeAdd(5, 7)
    println("Sum from native: $sum")
}

此示例显示了一个将两个整数相加的原生函数。 对应的 C 函数将被命名为 Java_com_zetcode_JNIFunction_nativeAdd。 在调用原生函数之前,会加载库。

external 属性

属性也可以标记为 external。 getter 和 setter 必须在原生代码中实现。 这对于访问原生状态很有用。

NativeProperty.kt
package com.zetcode

external var nativeCounter: Int

fun main() {
    System.loadLibrary("nativeLib")
    nativeCounter = 10
    println("Counter value: $nativeCounter")
}

在这里,我们声明一个由原生代码支持的 external 属性。 原生实现必须同时提供 getter 和 setter 函数。 该属性的行为类似于常规的 Kotlin 属性。

external 类

整个类可以标记为 external。 这种类的所有成员也必须是 external。 这对于 JNI 包装器很常见。

NativeClass.kt
package com.zetcode

external class NativeMath {
    fun add(a: Int, b: Int): Int
    fun subtract(a: Int, b: Int): Int
}

fun main() {
    System.loadLibrary("nativeLib")
    val math = NativeMath()
    println("5 + 3 = ${math.add(5, 3)}")
    println("5 - 3 = ${math.subtract(5, 3)}")
}

这个 external 类声明了在原生代码中实现的方法。 原生实现必须提供所有声明的方法。 该类可以像任何常规的 Kotlin 类一样使用。

JavaScript 互操作

当目标是 JavaScript 时,external 用于从 Kotlin 代码访问 JavaScript API。 声明直接映射到 JavaScript 对象。

JSInterop.kt
package com.zetcode

external fun alert(message: String)

external val document: dynamic

fun main() {
    alert("Hello from Kotlin/JS!")
    document.getElementById("demo").innerHTML = "Updated"
}

在 Kotlin/JS 中,external 声明映射到 JavaScript API。 这里我们声明了 alert 函数和 document 对象。 dynamic 类型允许灵活访问 JavaScript 属性。

external 伴生对象

伴生对象可以标记为 external,以便为静态方法提供原生实现。 这对于实用程序函数很有用。

NativeCompanion.kt
package com.zetcode

class NativeUtils {
    companion object {
        external fun getSystemTime(): Long
    }
}

fun main() {
    System.loadLibrary("nativeLib")
    val time = NativeUtils.getSystemTime()
    println("System time: $time")
}

此示例显示了一个 external 伴生对象函数。 原生实现将是一个静态 JNI 函数。 该函数通过伴生对象像静态方法一样被调用。

使用平台库的 external

external 关键字可以与特定于平台的库一起使用。 此示例演示了访问 Windows API 函数。

WindowsAPI.kt
package com.zetcode

external fun MessageBoxA(
    hWnd: Int,
    text: String,
    caption: String,
    uType: Int
): Int

fun main() {
    System.loadLibrary("user32")
    MessageBoxA(0, "Hello from Kotlin", "Message", 0)
}

在这里,我们将 Windows MessageBoxA API 声明为 external。 该函数从 user32.dll 加载。 这演示了 Kotlin 中的特定于平台的互操作性。

external 的最佳实践

来源

Kotlin Native 互操作文档

本教程深入探讨了 Kotlin 的 external 关键字,展示了它在 JNI、JavaScript 和平台互操作性中的应用。 我们探讨了各种场景,包括函数、属性、类和伴生对象。 正确使用 external 可以实现强大的原生集成。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有多年的编程经验。 自 2007 年以来,我一直在撰写编程文章。到目前为止,我写了超过 1400 篇文章和 8 本电子书。 我拥有超过八年的编程教学经验。

列出 所有 Kotlin 教程