【问题标题】:Lambda expression doesn't working in seemingly identical kotlinLambda 表达式在看似相同的 kotlin 中不起作用
【发布时间】:2019-06-12 09:27:11
【问题描述】:

在尝试创建一个简单的数据库助手时,我想填写来自 Spring 的 RowMapper<T>,但 Kotlin 坚持使用 KFunction2,即使 RowMapper 的接口匹配。这适用于 Spring Java 代码的原始接口。

在我看来,这些方法在重要部分似乎是相同的。但是,Kotlin 不同意我的观点。

我的界面(kotlin)如下所示:

interface MyJdbcOperations : NamedParameterJdbcOperations {
    fun <T> querySingle(
        sql: String,
        parameters: MapSqlParameterSource,
        rowMapper: RowMapper<T>): Optional<T>
}

NamedParameterJdbcOperations里面的查询界面如下(Java):

public interface NamedParameterJdbcOperations {
    // ... rest of interface omitted for readability

    <T> List<T> query(String sql, SqlParameterSource paramSource, RowMapper<T> rowMapper)
            throws DataAccessException;
}

但是当我使用我的MyJdbcOperations 实现如下:

template.query<Foo>(sql, parameters, this::mapToFoo)
template.querySingle<Foo>(sql, parameters, this::mapToFoo)

第一个版本可以,没问题,我也可以删除&lt;Foo&gt;,让类型推断完成它的工作。

然而,第二个版本给了我这样的错误:

Error:(25, 54) Kotlin: Type mismatch: inferred type is KFunction2<@ParameterName ResultSet, @ParameterName Int, Foo> but RowMapper<Foo> was expected

但我实际上希望这能正常工作,如果 KFunction2 签名可以满足 Java 接口中的 RowMapper&lt;Foo&gt;,那么我当然希望它也能够在 Kotlin 中实现。显然不是这样的。

那么我做错了什么,最好的解决方法是什么?

【问题讨论】:

    标签: java spring spring-boot kotlin


    【解决方案1】:

    我看到的工作用例和编译错误调用之间的主要区别在于,第二个引用定义在 Kotlin 接口中的方法,而第一个引用定义在 Java 接口中的方法。

    我知道 SAM 转换仅发生从 lambas/方法到 Java 接口,如下所述:https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions。 我不知道为什么这会影响您的方案,但是如果您将 MyJJdbcOperations 重新定义为 Java 接口,问题就会消失...

    这不是一个真正的解决方案,但它是一种解决方法。

    似乎 lambdas(我也尝试使用像 {x -&gt; this.mapToFoo(x)} 这样的 labdas,结果与您已经遇到的相同)和传递给 Kotlin 接口方法的方法被转换为 KFunctionN 而不是预期的接口,当期望的接口被定义为Java接口...

    这不是一个完整的答案,但我希望这可以帮助您更接近解决方案。

    【讨论】:

    • 为什么这个答案不完整?它完美地解释了它。也许正确的方法是不使用 RowMapper 作为参数类型,而是在 kotlin 接口中使用 rowMapper: (rs: ResultSet, rowNum: int) -&gt; T 作为最后一个参数类型?来自您链接的同一来源:“另请注意,此功能仅适用于 Java 互操作;由于 Kotlin 具有正确的函数类型,因此不需要将函数自动转换为 Kotlin 接口的实现,因此不受支持”
    • @sfiss 我倾向于同意,它确实相当完整 :) 我认为唯一缺少的部分实际上是他的回答让我添加了 :) 使用我认为使用 RowMapper 是不过,这完全是正确的做法。首先,它传达特定的意图和上下文。不过我想知道的是:有没有办法给正确的函数类型命名并记录它们?如果不是,我倾向于争辩说他们声称这是“不必要的”可能是错误的
    • 可能是类型别名? kotlinlang.org/docs/reference/type-aliases.htmlOfc,RowMapper typealias 与 spring 的 RowMapper 不同。
    • @sfiss 哦,谢谢,我一直在寻找 :) 是的,绝对比没有好,但不完全相同 :)
    【解决方案2】:

    我找到了至少一种解决方法,但我仍然对为什么会发生这种情况以及为什么它不能按预期工作感到困惑。以下将解决眼前的问题:

        template.querySingle<Foo>(sql, parameters, RowMapper { rs, rowNum -> this.mapToFoo(rs, rowNum) })
    

    感谢@pietro-martinelli 发布了一个帮助我走上正确道路的答案:)

    【讨论】:

      猜你喜欢
      • 2015-06-21
      • 1970-01-01
      • 2012-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-01
      • 1970-01-01
      相关资源
      最近更新 更多