从 Kotlin 调用 Java 时可为空的规则是什么

Vad*_*kan 3 nullable kotlin

为什么 Kotlin 在一种情况下推断从 Java 返回的类型可以为 null,而在另一种情况下它可以是可为 null 或不可为 null?

我已经检查了两者HashMap.getJsonNode.get而且我无法在 calsses 中或继承链中的任何位置识别任何类似 @NotNull 的注释。是什么让 Kotlin 以不同的方式对待这两个调用?

我已阅读文档https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types但它解释使用“平台类型”,但没有解释这些是什么,也没有解释行为差异反正。

import com.fasterxml.jackson.databind.JsonNode

private fun docType(node: JsonNode, map: java.util.HashMap<String,String>) {
    val x: JsonNode = node.get("doc_type")  // DOES compile and can throw NPE at runtime
    val y: JsonNode? = node.get("doc_type") // DOES compile and Kotlin's type system will force you to check for null
    val z: String = map.get("a")            // ERROR: Type mismatch: inferred type is String? but String was expected
}
Run Code Online (Sandbox Code Playgroud)

aSe*_*emy 5

Kotlin 提供了与 Java 的无缝互操作性,几乎不影响其自身的空安全性。一个例外是 Kotlin 假定Java 中定义的所有类型都不为 null。

\n

为了理解,让我们看看JsonNode.get()

\n

平台类型

\n
public JsonNode get(String fieldName) { return null; }\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,JsonNode是在 Java 中定义的,因此是一种平台类型- Kotlin 不会将其“翻译”为JsonNode?,即使这在技术上是正确的(因为在 Java 中所有类型都可为空)。

\n

从 Kotlin 调用 Java 时,为了方便起见,假设平台类型不可为 null。如果不是这种情况,您将始终必须检查任何平台类型的任何实例是否不为空。

\n

因此,要回答您关于“平台类型”是什么的问题,它是一个术语,意思是

\n
    \n
  • 用外部目标语言定义的某种类型,
  • \n
  • 你不能在 Kotlin 代码中明确提及它(但可能有一个同义的 Kotlin 等效项),
  • \n
  • 为了方便起见,我们将假设它不可为空。
  • \n
  • <type>!例如,符号是String!- 我们可以将其理解为String or String?
  • \n
\n

可空性注释

\n

与 Kotlin 可空?符号最接近的 Java 等效项是可空性注释,Kotlin 编译器可以解析并考虑该注释。但是,没有一个用于JsonNode方法。所以 Kotlin 会很高兴地假设node.get("")会返回JsonNode,而不是JsonNode?

\n

正如您所指出的,没有为 定义HashMap.get(...)

\n

那么 Kotlin 如何知道map.get("a")返回可空类型呢?

\n

类型推断

\n

类型推断无济于事。(Java) 方法签名

\n
public V get(Object key) {\n  //...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

表明 aHashMap<String, String>应该返回String,而不是String?。肯定还有其他事情发生...

\n

映射类型

\n

对于大多数 Java 类型,Kotlin 将仅使用所提供的定义。但对于某些,Kotlin 决定特殊对待它们,并用自己的版本完全替换 Java 定义。

\n

您可以在文档中查看映射类型的列表。虽然HashMap里面不存在,但Map确实存在。因此,当我们编写 Kotlin 代码时,HashMap不会继承java.util.Map- 因为它映射到kotlin.collections.Map

\n
\n

旁白:事实上,如果您尝试使用,java.util.Map您会收到警告使用 java.util.Map 的代码带有 IntelliJ 警告:此类不应在 Kotlin 中使用。 请改用 kotlin.collections.Map 或 kotlin.collections.MutableMap。

\n
\n

因此,如果我们查看定义的get函数的代码kotlin.collections.Map我们可以看到它返回一个可为空的值类型

\n
/**\n* Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.\n*/\npublic operator fun get(key: K): V?\n
Run Code Online (Sandbox Code Playgroud)\n

因此 Kotlin 编译器可以查看HashMap.get(...)并推断出,因为它正在实现kotlin.collections.Map.get(...),所以返回的值必须是可以为 null 的值,在我们的例子中是String?

\n

解决方法:外部注释

\n

无论出于何种原因,杰克逊没有使用可以解决此问题的可为空注释。幸运的是,IntelliJ 提供了一种解决方法,虽然不那么严格,但会提供有用的警告:外部注释

\n

一旦我按照说明操作...

\n
    \n
  1. Alt+Enter \xe2\x86\x92 \'注释方法...\'

    \n

    在此输入图像描述

    \n
  2. \n
  3. 选择“可为空”注释

    \n

    在此输入图像描述

    \n
  4. \n
  5. 节省annotations.xml

    \n
  6. \n
\n

现在node.get("")将显示警告。

\n

在此输入图像描述

\n

此注释对 Kotlin 编译器不可见,因此它只能是警告,而不是编译错误。

\n