在 Kotlin 中调用 Java¶
Kotlin 设计时就把 Java 互操作作为核心目标。绝大多数 Java 代码可以直接从 Kotlin 调用,但“能调用”不代表“语义完全一样”。真正需要理解的是属性映射、平台类型、集合可变性、关键字转义和泛型空性。
直接调用 Java 类¶
Java:
public class UserService {
public String findName(long id) {
return "Ada";
}
}
Kotlin:
val service = UserService()
val name = service.findName(1)
println(name)
Kotlin 会自然识别 Java 类、构造函数、方法和字段。
getter/setter 映射为属性¶
Java:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Kotlin 调用:
val user = User()
user.name = "Ada"
println(user.name)
user.name 实际调用 getName() / setName()。这类属性称为 synthetic property。
布尔 getter 也会映射:
public boolean isEnabled() { return true; }
public void setEnabled(boolean enabled) {}
Kotlin:
if (config.isEnabled) {
config.isEnabled = false
}
如果 Java 类只有 setter,没有 getter,Kotlin 不会把它暴露成属性,因为 Kotlin 不支持“只写属性”。
Java void 与 Kotlin Unit¶
Java 返回 void 的方法,在 Kotlin 中返回 Unit:
public void save() {}
Kotlin:
val result = service.save() // result 类型是 Unit
通常你不会使用这个返回值。它存在是为了让 Kotlin 的表达式和函数类型系统保持统一。
Java 标识符与 Kotlin 关键字冲突¶
如果 Java 方法名刚好是 Kotlin 关键字,可以用反引号调用:
foo.`is`(bar)
foo.`object`()
这类情况不常见,但在调用老 Java API、代码生成 API 或特殊 DSL 时可能遇到。
平台类型¶
Java 引用可能为 null,但 Java 类型本身通常不表达空性。Kotlin 调用未标注空性的 Java API 时,会得到平台类型。
Java:
public String findName(long id) {
return null;
}
Kotlin:
val name = api.findName(1)
println(name.length) // 编译器可能允许,但运行时可能 NPE
IDE 可能把它显示为 String!。你不能在 Kotlin 源码中显式写 String!,它只是编译器和 IDE 表示“不知道是 String 还是 String?”的方式。
建议在边界处立刻收敛类型:
val name: String? = api.findName(1)
val displayName = name ?: "匿名"
或如果业务上必须非空:
val name: String = api.findName(1)
?: error("Java API 返回了空 name")
空性注解¶
Kotlin 可以识别多种 Java 空性注解,例如:
- JetBrains
@Nullable/@NotNull - JSpecify
@Nullable/@NonNull/@NullMarked - Android 注解
- JSR-305 注解
- Eclipse、FindBugs、Lombok、RxJava 相关注解
Java:
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
public class UserApi {
public @Nullable String findNickname(long id) {
return null;
}
public @NotNull String findName(long id) {
return "Ada";
}
}
Kotlin:
val nickname: String? = api.findNickname(1)
val name: String = api.findName(1)
对 Java 库维护者来说,补充空性注解是改善 Kotlin 体验最有效的方式之一。
Java 集合映射¶
Java 集合在 Kotlin 中会映射到 Kotlin 集合接口,但因为 Java 集合本身可变性不明确,经常显示为类似 (Mutable)List<T>! 的平台形式。
val list = javaApi.loadNames()
你需要根据 API 语义决定:
val readOnly: List<String> = javaApi.loadNames()
val mutable: MutableList<String> = javaApi.loadNames()
如果 Java 方法没有明确承诺可变性,不要随意把返回值当作可变集合修改。必要时复制:
val mutableNames = javaApi.loadNames().toMutableList()
Java 数组¶
Java 数组可以从 Kotlin 调用:
val array: Array<String> = javaApi.loadArray()
println(array[0])
基本类型数组有专门类型:
IntArrayLongArrayDoubleArrayBooleanArray
它们避免装箱,类似 Java 的 int[]、long[]。
SAM 转换¶
Java 函数式接口可以用 Kotlin Lambda 调用:
Java:
public interface Callback {
void onDone(String value);
}
public void load(Callback callback) {}
Kotlin:
api.load { value ->
println(value)
}
这是 Kotlin 调 Java API 时非常常见的写法,尤其在旧 Java 回调接口、Swing、Android、Executor 等场景中。
checked exception¶
Java 方法声明的 checked exception 不会强制 Kotlin 捕获:
public String read() throws IOException {}
Kotlin:
val text = reader.read() // 编译器不强制 try-catch
这让调用更简洁,但也意味着你要根据业务场景主动处理异常。不要因为 Kotlin 不强制,就忽略真实的失败路径。
实践建议¶
- 调 Java API 的返回值时,优先显式收敛平台类型。
- 能给 Java 代码补空性注解就补,尤其是公共库和核心模块。
- Java 集合返回值不明确时,按需要
toList()或toMutableList()。 - checked exception 不强制捕获,但设计上仍要处理 IO、网络、数据库等失败。
- 关键业务边界不要让平台类型深入 Kotlin 核心领域模型。
参考¶
- 官方文档:Calling Java from Kotlin:https://kotlinlang.org/docs/java-interop.html