7.2.2 从 Kotlin 使用 JavaScript 代码
4 分钟阅读
7.2.2 从 Kotlin 使用 JavaScript 代码
Kotlin 最初就是为与 Java 平台轻松互操作而设计的:它把 Java 类视为 Kotlin 类,而 Java 也把 Kotlin 类视为 Java 类。
不过,JavaScript 是动态类型语言,也就是说它不会在编译期检查类型。你可以通过 dynamic 类型自由地与 JavaScript 交互。如果你想利用 Kotlin 类型系统的全部能力,可以为 JavaScript 库创建外部声明,Kotlin 编译器和相关工具链都能理解它们。
内联 JavaScript
你可以使用 js() 函数把 JavaScript 代码内联到 Kotlin 代码中:
| |
JavaScript 代码内联完整支持 ES2015 特性,包括:
const和let变量声明- ES 类
- 生成器
- Lambda(箭头函数)
- 展开与剩余运算符
- 模板字符串
由于 js 的参数是在编译期解析并"原样"翻译成 JavaScript 代码的,它必须是字符串常量。因此下面的代码不正确:
| |
反之,举例来说,要内联剩余运算符,请使用字符串常量:
| |
注意: 调用
js()会返回dynamic类型的结果,它在编译期不提供类型安全。
external 修饰符
要告诉 Kotlin 某个声明是用纯 JavaScript 编写的,你应该用 external 修饰符标记它。当编译器看到这样的声明时,它会假定相应类、函数或属性的实现由外部提供(由开发者提供,或通过 npm 依赖),因此不会尝试从该声明生成任何 JavaScript 代码。这也是 external 声明不能有函数体的原因。例如:
| |
注意 external 修饰符会被嵌套声明继承。这就是为什么在例子中的 Node 类里,成员函数和属性前面没有 external 修饰符。
external 修饰符只允许用在包级声明上。你不能声明非 external 类的 external 成员。
声明类的(静态)成员
在 JavaScript 中,你既可以在原型上定义成员,也可以在类自身上定义成员:
| |
Kotlin 中没有这样的语法。不过在 Kotlin 中我们有 companion 对象。Kotlin 对 external 类的伴生对象有特殊处理:它不期望那里有一个对象,而是把伴生对象的成员视为类自身的成员。上面例子中的 MyClass 可以这样描述:
| |
声明带默认值的参数
如果你要为带有默认值参数的 JavaScript 函数编写外部声明,请使用 definedExternally。它把默认值的生成委托给该 JavaScript 函数本身:
| |
有了这个外部声明,你可以用一个必填实参和两个可选实参调用 myFunWithOptionalArgs,其中默认值由 myFunWithOptionalArgs 的 JavaScript 实现计算。
继承 JavaScript 类
你可以像继承 Kotlin 类一样轻松地继承 JavaScript 类。只需定义一个 external open 类,并用一个非 external 类继承它。例如:
| |
有一些限制:
- 当外部基类的某个函数按签名重载时,你无法在派生类中重写它。
- 你不能重写包含带默认值参数的函数。
- 非 external 类不能被 external 类继承。
external 接口
JavaScript 没有接口的概念。当一个函数期望其参数支持 foo 和 bar 两个方法时,你只需传入一个实际拥有这些方法的对象。
你可以使用接口在静态类型的 Kotlin 中表达这一概念:
| |
external 接口的一个典型用例是描述设置对象。例如:
| |
external 接口有一些限制:
- 它们不能用在
is检查的右侧。 - 它们不能作为具体化类型实参传递。
- 它们不能用在类字面量表达式中(例如
I::class)。 - 到 external 接口的
as转换总是成功。转换到 external 接口会产生"Unchecked cast to external interface"编译期警告。该警告可以用@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")注解抑制。
IntelliJ IDEA 也可以自动生成 @Suppress 注解。通过灯泡图标或 Alt-Enter 打开意图菜单,然后点击 “Unchecked cast to external interface” 检查旁边的小箭头。在这里你可以选择抑制范围,IDE 会相应地把注解添加到你的文件中。
类型转换
除了在无法转换时抛出 ClassCastException 的“不安全"转换运算符 as 之外,Kotlin/JS 还提供了 unsafeCast<T>()。使用 unsafeCast 时,运行时_完全不进行类型检查_。例如,考虑下面两个方法:
| |
它们会被相应编译为:
| |
相等性
与其他平台相比,Kotlin/JS 在相等性检查方面有特殊的语义。
在 Kotlin/JS 中,Kotlin 的引用相等性运算符(===)总是翻译为 JavaScript 的严格相等运算符(===)。
JavaScript 的 === 运算符不仅检查两个值是否相等,还会检查这两个值的类型是否相同:
| |
此外,在 Kotlin/JS 中,Byte、Short、Int、Float 和 Double 这些数值类型在运行时都用 JavaScript 的 Number 类型表示。因此这五种类型的值无法区分:
| |
提示: 关于 Kotlin 中相等性的更多信息,请参阅相等性文档。