【问题标题】:Kotlin Map type and the return value of JdbcTemplate.queryForMap()Kotlin Map 类型和 JdbcTemplate.queryForMap() 的返回值
【发布时间】:2021-01-15 11:29:26
【问题描述】:

我最近遇到一个 Kotlin/Spring Boot 显示我不完全理解的行为。

让我们从一个非常像教科书般的纯 Kotlin 示例开始:

val map: Map<String, Any> = mapOf(
   "name": "John",
   "age": 21,
   "job": null
)

上面的代码 sn-p 将导致编译器错误,这是可以理解的,提醒我们由于那个 ("job", null) 对,我们实际上分配了一个 Map到 Map 类型的变量,这是不允许的。

现在假设我们在 Spring Boot 中使用 Kotlin。我们还假设我们有一个名为 PERSON 的数据库(例如 MySQL)表,其中包含三列:

NAME of type varchar (not nullable)
AGE of type int (not nullable)
JOB of type varchar (nullable)

我们还假设我们的表中有一行的数据与我们之前提到的示例数据匹配:

'John', 21, NULL

使用 JdbcTemplate 实例,我们可以执行以下操作:

val map: Map<String, Any> = jdbcTemplate.queryForMap("SELECT * FROM PERSON WHERE NAME=John")
println(map)

对我来说,令人惊讶的结果是,最后的 sn-p 代码没有产生任何错误并打印出来

{NAME=John, AGE=21, JOB=null}

所以看起来我们成功地在 Map 中包含了一个 Pair

这是故意的吗?其背后的逻辑是什么?

【问题讨论】:

    标签: spring-boot kotlin jdbctemplate


    【解决方案1】:

    这是一个 Java 互操作性问题。

    JVM 本身没有不可为空的类型。 Kotlin 编译器通过在编译时跟踪类型来实现这些。

    这在纯 Kotlin 代码中非常有效,但在与用 Java 或其他 JVM 语言编写的代码互操作时会出现一些极端情况。 Kotlin 编译器有一些变通方法:它使用并识别 standard annotations 来指示可空性;一些 Java 代码使用这些(并且 IDE 可以使用它们来显示警告)。当可以从非 Kotlin 代码传递空值时,它会插入检查。它使用platform types 来指示何时无法确定非 Kotlin 代码中的值的可空性。但是,它无法应对所有情况。

    特别是,由于type erasure,泛型已经有各种尴尬的极端情况。这就是我们所拥有的:org.springframework.jdbc.core.JdbcTemplate 类是用 Java 编写的,因此它也对不可为空的类型一无所知。问题出在类型参数中,这些参数被删除了,因此编译后的代码只知道原始的Map。所以 Kotlin 编译器无法判断返回对象的类型参数是否可以为空。

    这很复杂,因为您在代码中指定了返回类型。如果你让编译器推断它:

    val map = jdbcTemplate.queryForMap("SELECT * FROM PERSON WHERE NAME='John'")
    

    ...然后它将类型参数推断为&lt;String!, Any!&gt;!s 指示平台类型,它无法判断可空性。但是因为您已经指定了它们,所以编译器会假定您知道自己在做什么,并且不会给出任何进一步的警告或检查。

    我想这样做的意义是不要指定返回类型,除非您确定它是正确的,因为当与非 Kotlin 代码互操作时,编译器无法始终检查您是否正确!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-06
      • 2019-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多