Kotlin的DAO应该返回Optional还是null?

时间:2017-11-28 10:47:50

标签: kotlin spring-data spring-data-jpa dao

在Kotlin / JPA之前,我曾经写过这样的DAO层:

public interface UserDao extends JpaRepository<User,Long> {
    Optional<User> findBySsn(String ssn);
}

在来电方面,如果我想通过SSN找人或创建用户,我可以这样写:

val user = userDao.findBySsn(value).orElseGet {
    userDao.save(value)
}

效果很好,看起来很流利。

但是由于Kotlin引入了null安全性,还有另一种惯用方法(dao仍在Java中):

public interface UserDao extends JpaRepository<User,Long>  {

    Optional<User> findBySsn(String ssn);

    @Query("select u from User u where u.ssn = :ssn")
    @Nullable User findBySsnNullable(@Param("ssn") String ssn)
}

在客户端:

val user = userDao.findBySsnNullable(value)
      .takeIf{ it -> it != null}? : userDao.save(User(value))

两种方式都很好。但我想知道哪个更受欢迎? Kotlin在API设计中依赖Java8的Optional是否有益? Kotlin项目依赖于(或通过)Java8的Optional / Stream API(因为Kotlin有自己的API)的缺点是什么?

Kotlin可以编译成JavaScript(我还没有研究过)。如果项目依赖于Java Optional / Stream,那么它是否会编译成JS问题?

----更新----

根据Jetbrains

  

不,通用代码只能依赖于其他公共库。科特林有   不支持将Java字节码转换为JS。

2 个答案:

答案 0 :(得分:6)

如果你不必,我就不会使用Optional。它只会增加不必要的开销,因为使用可空类型在Kotlin中更具可读性和更具惯用性。在Kotlin中使用Optional没有任何优势。

以下是对此的另一个讨论:https://discuss.kotlinlang.org/t/java-api-design-for-kotlin-consumption-optional-or-null/2455

答案 1 :(得分:0)

我不得不说我不同意@ s1m0nw1

Kotlin所做的是为我们提供一种处理错误的好方法。 Kotlin无法消除该错误,因为它是JVM中固有的,并且会破坏与旧版Java和编写不良的Java的集成。但是,因为它有一个不错的工具来处理不良设计,并不意味着我们应该接受不良设计并自己完成。

一些推理:

  • 在从Java调用Kotlin代码后,Nullable仍然会遇到相同的问题。现在,您只是掩盖了潜在的问题,从而主动损害了客户。 -就其本身而言,这是IMO的充分理由。

  • Null仍然是模棱两可的,null是什么意思?错误?价值缺失?要么? Null没有明确的含义,即使您为它指定了含义,也必须在各处记录下来,并希望人们能读懂它。 Value.EMPTY,Value.MISSING,引发新的Exception()都具有(某种程度上)明确定义的含义,这些含义可以从您的代码中清晰地读取。这与将布尔值用作二进制枚举值的快捷方式一样,例如:

val testPassed = runTest()

if (testPassed) // ...

这具有明确的含义,只要您具有变量名。传递,重构等都可以很快混淆它。用户的意思:

val testResult = runTest()
if (testResult == TestResult.PASSED)

清晰易读。因此,根据相同的论点,让您的代码传达您的意图。不要使用快捷方式。再次,我认为Kotlin与null的处理非常好,对于处理不良代码,我不认为这是产生不良代码的借口。

  • Null从逻辑上讲是不合逻辑的,从本质上讲,它是没有指向的指针。基本上,这是一个无意义的“值”,实际上并不是一个值。没什么

  • 对于null,您仍然必须为利用各种流功能的API做一些奇怪的事情,因此,通过使用null,您将不得不做s1m0nw1链接到的辩论中所示的骇人的事情,或者进行显式转换无论如何,因此您无论如何都会同时拥有它们,通过选择null完全不保存任何内容,但最终会处理nullable和Optional的语义。

  • 这是确保您返回Optional的最低工作量。我的意思是说一个,我们在这里不是在谈论很多额外的代码。不要害怕写一些额外的行来正确地做事情。可读性,灵活性等应放在短代码之前。仅仅因为有一个新的智能功能可以立即编写内容并避免错误,并不意味着您必须一直使用它。请记住,用锤子把所有东西都看成是钉子:)

尽管不执行null的问题是Java中缺少适当的“也许”类型。更为正确的解决方案是功能语言使用的Option / Maybe类型,但是Java的Optional仅占一半。它不能完全捕获真正的Maybe / Option类型的语义,这就是为什么您不应该对参数和字段使用Optional的原因。

对于参数而言,这不是问题,因为您可以轻松地确保不使用该参数开头的重载-在像Kotlin这样的语言中,此任务甚至更容易。

对于字段来说,当前的“解决方案”似乎是定义一个适当的Maybe / Option-type(也许特定于您的每种类型),以确保消除类型和序列化不会妨碍您。您可以使用NullObject模式,这似乎有点丑陋,无法做到完全相同。或者具有讨厌的null值,但将其完全封装在您的类中。到目前为止,我一直在做最后一件事,但是我不喜欢它:/但是实用主义才有它的位置;)