为什么Kotlin在一种情况下推断从Java返回的类型是可空的,而在另一种情况下它可以是可空的或不可空的?
我已经检查了HashMap.get
和JsonNode.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
}
2条答案
按热度按时间yiytaume1#
Kotlin提供了与Java的无缝互操作性,而不会损害其自身的null-safety......几乎。一个例外是Kotlin假设Java中定义的所有类型都不是null。
为了理解,让我们看一下
JsonNode.get()
平台类型
请注意,
JsonNode
是在Java中定义的,因此是一个平台类型-Kotlin不会将其“翻译”为JsonNode?
,即使这在技术上是正确的(因为在Java中所有类型都是空的)。当从Kotlin调用Java时,for convenience假设平台类型是不可空的。如果不是这样,你将 * 总是 * 必须检查任何平台类型的任何示例都不是null。
所以,为了回答你关于什么是“平台类型”的问题,这个术语的意思是
<type>!
,例如String!
-我们可以将其视为String or String?
空性标注
与Kotlin的可空
?
符号最接近的Java等价物是可空性注解,Kotlin编译器可以解析并考虑这些注解。然而,在JsonNode
方法上没有使用任何注解。因此Kotlin会很高兴地假设node.get("")
将返回JsonNode
,而不是JsonNode?
。正如您所注意到的,没有为
HashMap.get(...)
定义任何内容。那么Kotlin是如何知道
map.get("a")
返回一个可空类型的呢?类型推断
类型推断也无济于事。(Java)方法签名
指示
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
函数的代码,我们可以看到它返回一个可空值类型因此Kotlin编译器可以查看
HashMap.get(...)
并推断,因为它实现了kotlin.collections.Map.get(...)
,所以返回值 * 必须 * 是一个可空值,在我们的例子中是String?
。解决方法:外部注解
不管出于什么原因,Jackson没有使用可以解决这个问题的空性注解。幸运的是,IntelliJ提供了一个解决方案,虽然不那么严格,但会提供有用的警告:external annotations。
一旦我按照指示去做…
1.选择“可为空”注解
1.保存
annotations.xml
现在
node.get("")
将显示一个警告。此注解对Kotlin编译器不可见,因此它只能是一个警告,而不是编译错误。
lf5gs5x22#
java.util.HashMap.get
实现了接口方法java.util.Map.get
。Kotlin在内部将一些Java类型Map到自己的类型。这些Map的完整表可以在网站上找到。在我们的特定示例中,我们看到java.util.Map
在内部Map到kotlin.collections.Map
,其get
函数如下所示因此,就Kotlin而言,
java.util.Map
只是kotlin.collections.Map
的一个有趣的名字,java.util.Map
上的所有方法实际上都有来自kotlin.collections.Map
的相应方法的签名(除了正确的null注解外,它们基本上是相同的)。因此,虽然前两个
node.get
调用是Java调用并返回平台类型,但第三个(就Kotlin而言)实际上是调用Kotlin理解的方法:也就是说,get
来自 * 它自己的 *Map
类型。并且该类型已经有一个显式的可空性注解,因此Kotlin可以自信地说该值 * 可以 * 是null
并且需要检查。