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

我已经判断了HashMap.getJsonNode.get,无论是在CALSS中还是在继承链的任何地方,我都无法识别任何类似于@NotNull的注释.是什么让Kotlin对这两个电话的处理方式有所不同?

我已经阅读了文档https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types,但it解释使用了"平台类型",而没有解释这些类型是什么,而且它也没有解释行为上的差异.

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
}

推荐答案

Kotlin编译器有几种执行空判断的方法.

其中之一是解析nullability annotations.正如您所注意到的,HashMap.get(...)没有定义.

但是,正如错误所示,这与"推断类型"有关

  val z: String = map.get("a")            
  // ERROR: Type mismatch: inferred type is String? but String was expected

Kotlin可以进行"类型推断",即documented in the spec.

Kotlin有一个编译时类型信息的类型推断概念,这意味着代码中的一些类型信息可能会被省略,由编译器进行推断.Kotlin支持两种类型的推理.

因为HashMap<K, V>是泛型的,所以很容易确定具有(Java)签名的函数

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

must返回类型V.

在本例中,因为在Kotlin中,您将V定义为String,而不是String?,所以编译器可以确定VStringnotString?.

Read the spec regarding function signature type inference

JsonNode是不同的.

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

请注意,JsonNode是在Java中定义的,因此是'platform type',Kotlin不会将其"翻译"为JsonNode? for convenience,即使这在技术上是正确的.也没有可空性注释.所以Kotlin很乐意相信node.get("")不会返回JsonNode?.

解决方法:外部注释

无论出于何种原因,Jackson都没有使用可以解决此问题的nullability注释.幸运的是,IntelliJ提供了一种变通方法,虽然没有那么严格,但可以提供有用的警告:external annotations.

一旦我按照指示行事...

  1. Alt+Enter→ '注释方法…'

    enter image description here

  2. Select "Nullable"批注

    enter image description here

  3. 保存annotations.xml

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

enter image description here

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

Kotlin相关问答推荐

如何在Kotlin中反射多个对象以查找特定类型的属性

Kotlin—从列表中枚举属性计算不同值的数量

在Kotlin中,有没有一种函数方法将一个列表(N个元素)映射到一个相邻元素之和列表(N—1个元素)?

在kotlin中使用List(mylist. size){index—TODO()}或Map迭代>

如何为集成测试配置Gradle JVM测试套件?

"Kotlin中的表达式

数据源配置

如何检测一个值是否是Kotlin中的枚举实例?

为什么 trySend 会发出假数据?

使用启动或使用 coroutineScope 启动协程之间的区别

如何从 kotlin 中的数据类访问 val?

为空数组添加值

如何在 IntelliJ 中更改 Kotlin 的this property has a backing field代码编辑器突出显示?

如何处理 Kotlin 中的异常?

在java代码中使用kotlin库

如何使用kotlin中的反射查找包中的所有类

用于代码生成的ANTLR工具版本4.7.1与当前运行时版本4.5.3不匹配

指定为非null的参数在ArrayAdaper中为null

尾随 lambda 语法(Kotlin)的目的是什么?

如何将 Kotlin 的 `with` 表达式用于可空类型