从Kotlin调用Java时有哪些可空规则

pvcm50d1  于 2023-04-21  发布在  Kotlin
关注(0)|答案(2)|浏览(106)

为什么Kotlin在一种情况下推断从Java返回的类型是可空的,而在另一种情况下它可以是可空的或不可空的?
我已经检查了HashMap.getJsonNode.get,我无法在类和继承链中识别任何类似@ 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
}
yiytaume

yiytaume1#

Kotlin提供了与Java的无缝互操作性,而不会损害其自身的null-safety......几乎。一个例外是Kotlin假设Java中定义的所有类型都不是null。
为了理解,让我们看一下JsonNode.get()

平台类型

public JsonNode get(String fieldName) { return null; }

请注意,JsonNode是在Java中定义的,因此是一个平台类型-Kotlin不会将其“翻译”为JsonNode?,即使这在技术上是正确的(因为在Java中所有类型都是空的)。
当从Kotlin调用Java时,for convenience假设平台类型是不可空的。如果不是这样,你将 * 总是 * 必须检查任何平台类型的任何示例都不是null。
所以,为了回答你关于什么是“平台类型”的问题,这个术语的意思是

  • 一些用外部目标语言定义类型,
  • 你不能在Kotlin代码中明确提到它(但可能有一个同义的Kotlin等价物),
  • 为了方便我们假设它是不可空的。
  • 符号也是<type>!,例如String!-我们可以将其视为String or String?

空性标注

与Kotlin的可空?符号最接近的Java等价物是可空性注解,Kotlin编译器可以解析并考虑这些注解。然而,在JsonNode方法上没有使用任何注解。因此Kotlin会很高兴地假设node.get("")将返回JsonNode,而不是JsonNode?
正如您所注意到的,没有为HashMap.get(...)定义任何内容。
那么Kotlin是如何知道map.get("a")返回一个可空类型的呢?

类型推断

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

public V get(Object key) {
  //...
}

指示HashMap<String, String>应该返回String,而不是String?。一定有其他原因...

Map类型

对于大多数Java类型,Kotlin将只使用提供的定义。但对于某些类型,Kotlin决定特殊对待它们,并用自己的版本完全替换Java定义。
您可以在文档中看到Map类型的列表。虽然HashMap不在其中,但Map在其中。因此,当我们编写Kotlin代码时,HashMap不会从java.util.Map继承-因为它Map到kotlin.collections.Map
旁白:事实上,如果你尝试使用java.util.Map,你会得到一个警告x1c 0d1x。
因此,如果我们查看kotlin.collections.Map定义的get函数的代码,我们可以看到它返回一个可空值类型

/**
* Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.
*/
public operator fun get(key: K): V?

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

解决方法:外部注解

不管出于什么原因,Jackson没有使用可以解决这个问题的空性注解。幸运的是,IntelliJ提供了一个解决方案,虽然不那么严格,但会提供有用的警告:external annotations
一旦我按照指示去做…

  1. Alt+Enter →“注解方法...”

1.选择“可为空”注解

1.保存annotations.xml
现在node.get("")将显示一个警告。

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

lf5gs5x2

lf5gs5x22#

java.util.HashMap.get实现了接口方法java.util.Map.get。Kotlin在内部将一些Java类型Map到自己的类型。这些Map的完整表可以在网站上找到。在我们的特定示例中,我们看到java.util.Map在内部Map到kotlin.collections.Map,其get函数如下所示

abstract operator fun get(key: K): V?

因此,就Kotlin而言,java.util.Map只是kotlin.collections.Map的一个有趣的名字,java.util.Map上的所有方法实际上都有来自kotlin.collections.Map的相应方法的签名(除了正确的null注解外,它们基本上是相同的)。
因此,虽然前两个node.get调用是Java调用并返回平台类型,但第三个(就Kotlin而言)实际上是调用Kotlin理解的方法:也就是说,get来自 * 它自己的 * Map类型。并且该类型已经有一个显式的可空性注解,因此Kotlin可以自信地说该值 * 可以 * 是null并且需要检查。

相关问题