1. 概述
本文将介绍 Kotlin 中一个重要的概念:平台类型(Platform Types)。我们会解释其本质、如何处理平台类型带来的潜在空值问题,并展示 Java 中的注解如何帮助 Kotlin 做空值安全检查。
2. 平台类型的定义
在 Kotlin 中,平台类型是指从 Java 中引入的类型。由于 Java 并不强制空值检查,Kotlin 无法在编译期确定这些对象是否为 null。
这意味着:
✅ Kotlin 编译器无法对平台类型进行严格的空值检查
✅ 平台类型可能在运行时抛出 NullPointerException
✅ Kotlin 的空安全机制在这种情况下失效
平台类型通常用一个 !
标记来表示,例如 String!
表示这个字符串可能是 null 也可能是非 null。
3. 平台类型的空值处理
我们来看一个 Java 类:
public class Client {
private String name;
public Client() {
}
public String getName() {
return name;
}
}
上面的 getName()
返回的是一个 Java 的 String
,Kotlin 会将其视为 String!
。我们来测试一下直接访问这个值会发生什么:
@Test
fun givenNullable_whenCall_thenFail() {
val client = Client()
assertFailsWith(NullPointerException::class) {
client.name.length
}
}
⚠️ 注意:这里没有编译错误,但运行时抛出了 NullPointerException
,因为 name
是 null。
默认情况下,Kotlin 把平台类型视为非空类型。如果我们想安全使用它,应该显式地声明为可空类型并使用安全调用操作符:
@Test
fun givenNullable_whenCall_thenNotFail() {
val client = Client()
val name: String? = client.name
assertThat(name?.length).isNull()
}
✅ 通过将 client.name
显式赋值给 String?
类型,并使用 ?.
安全调用,我们就避免了空指针异常。
4. Java 注解支持空值检查
为了让 Kotlin 更好地理解 Java 类型的空值语义,我们可以使用 Java 中的注解来提供空值信息。这些注解能让 Kotlin 编译器知道某个变量是否可能为 null。
常见的支持注解包括:
- JetBrains:
@Nullable
,@NotNull
(org.jetbrains.annotations
) - Android:
@Nullable
,@NonNull
(androidx.annotation
) - JSpecify:
@NullMarked
,@ParametricNullness
- JSR-305:
@Nullable
,@Nonnull
- FindBugs:
@Nullable
,@NonNull
- Eclipse:
@NonNullByDefault
- Lombok:
@NonNull
- RxJava 3:
@Nullable
,@NonNull
这些注解会被 Kotlin 编译器识别。你也可以通过 -Xnullability-annotations
参数来配置注解包及其处理级别:
-Xnullability-annotations=@com.example.annotations:strict
其中处理级别包括:
ignore
:忽略空值不匹配warn
:警告strict
:报错(最推荐)
举个例子,我们给 Client.getName()
加上 @Nullable
注解:
@Nullable
public String getName() {
return name;
}
此时再运行之前的测试 givenNullable_whenCall_thenFail
,编译器就会报错:
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
✅ 这说明 Kotlin 编译器已经识别到这个方法可能返回 null,从而强制我们进行空值处理。
5. 总结
本文介绍了 Kotlin 中平台类型的本质及其带来的空值隐患,并演示了如何通过显式声明可空类型和使用安全调用操作符来处理这些问题。最后,我们还展示了 Java 中常见的空值注解,它们可以有效帮助 Kotlin 实现更严格的空值安全检查。
所有示例代码都可以在 GitHub 仓库 中找到。