Kotlin 的 DAO return 应该是 Optional 还是 null?
Should Kotlin's DAO return Optional or null?
在 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-safety ,还有另一种惯用的方式(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 有自己的)有什么缺点?
Kotlin 可以编译成 JavaScript(我没研究过)。如果项目依赖Java的Optional
/Stream
,编译成JS会不会有问题?
----更新了----
No, common code can only depend on other common libraries. Kotlin has
no support for translating Java bytecode into JS.
如果不需要,我不会使用 Optional
。它只会增加不必要的开销,因为在 Kotlin 中使用可空类型更具可读性和惯用性。在 Kotlin 中使用 Optional
没有任何优势。
这是另一个讨论:https://discuss.kotlinlang.org/t/java-api-design-for-kotlin-consumption-optional-or-null/2455
我不得不说我不同意@s1m0nw1
Kotlin 所做的是为我们提供了一种处理错误的好方法。 Kotlin 无法消除该错误,因为它在 JVM 中是固有的,并且会破坏与遗留 Java 和编写不当 Java 的集成。但是,因为它有一个很好的工具来处理糟糕的设计,并不意味着我们应该拥抱糟糕的设计并自己做。
一些推理:
Nullable 仍然会在第二次从 Java 调用 Kotlin 代码时出现同样的问题。现在你只是掩盖了潜在的问题,积极地损害你的客户。 - 就IMO而言,这本身就是一个足够强大的理由。
null还是有歧义,null是什么意思?错误?价值缺失?要么? null没有明确的含义,即使你给它赋予了含义,你也必须到处记录它并希望人们阅读它。 Value.EMPTY、Value.MISSING、throw new Exception() 都具有(某种程度上)明确定义的含义,可从您的代码中清楚地读取。这与将布尔值作为二进制枚举值的快捷方式的问题相同,例如:
val testPassed = runTest()
if (testPassed) // ...
这个意思很明确,只要有变量名就可以了。传递它、重构等可以很快混淆它。用户的意思:
val testResult = runTest()
if (testResult == TestResult.PASSED)
清晰易读。因此,根据相同的论点,让您的代码传达您的意图。不要走捷径。我再次认为 Kotlin 处理 null 非常好,对于处理糟糕的代码,我不认为它是产生糟糕代码的借口。
null在逻辑上是不合逻辑的,本质上它是一个不指向的指针。这基本上是一个废话 "value" 并不是真正的价值。这真的不是什么。
对于空值,您仍然需要为使用各种流功能的 API 做一些奇怪的事情,因此通过使用空值,您将不得不做一些棘手的事情,如争论 s1m0nw1 链接到,或者无论如何都进行显式转换,所以你最终会同时拥有它们,通过选择 null 完全没有节省任何东西,但最终处理了可为 nullable 和 Optional 的语义。
这是确保您 return 可选的最低工作量。我的意思是来一个,我们在这里不是在谈论很多额外的代码。不要害怕写一些额外的行来做正确的事情。可读性、灵活性等应该放在短代码之前。仅仅因为有一个新的智能功能可以快速编写内容并避免错误,并不意味着您必须一直使用它。请记住,对于锤子来说,一切看起来都像钉子:)
不执行 null 的问题是 Java 中缺少合适的 "Maybe" 类型。更正确的解决方案是函数式语言使用的 Option/Maybe 类型,但是 Java 的 Optional 只是一半的措施。它没有完全捕获真正 Maybe/Option 类型的语义,这就是为什么您不应该对参数和字段使用 Optional。
对于参数,这不是问题,因为您可以轻松确保不以该参数开头的重载 - 在 Kotlin 等语言中,这项任务甚至更容易。
对于字段,目前的 "solutions" 似乎要定义一个适当的 Maybe/Option 类型,可能特定于您的每种类型,以确保类型擦除和序列化不会妨碍您。您可以使用 NullObject 模式,这似乎是一种稍微丑陋的方式来完成完全相同的事情。或者有讨厌的 null 值,但将它完全封装在你的 class 中。到目前为止,我一直在做最后一个,但我不喜欢它:/但实用主义有它的一席之地;)
在 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-safety ,还有另一种惯用的方式(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 有自己的)有什么缺点?
Kotlin 可以编译成 JavaScript(我没研究过)。如果项目依赖Java的Optional
/Stream
,编译成JS会不会有问题?
----更新了----
No, common code can only depend on other common libraries. Kotlin has no support for translating Java bytecode into JS.
如果不需要,我不会使用 Optional
。它只会增加不必要的开销,因为在 Kotlin 中使用可空类型更具可读性和惯用性。在 Kotlin 中使用 Optional
没有任何优势。
这是另一个讨论:https://discuss.kotlinlang.org/t/java-api-design-for-kotlin-consumption-optional-or-null/2455
我不得不说我不同意@s1m0nw1
Kotlin 所做的是为我们提供了一种处理错误的好方法。 Kotlin 无法消除该错误,因为它在 JVM 中是固有的,并且会破坏与遗留 Java 和编写不当 Java 的集成。但是,因为它有一个很好的工具来处理糟糕的设计,并不意味着我们应该拥抱糟糕的设计并自己做。
一些推理:
Nullable 仍然会在第二次从 Java 调用 Kotlin 代码时出现同样的问题。现在你只是掩盖了潜在的问题,积极地损害你的客户。 - 就IMO而言,这本身就是一个足够强大的理由。
null还是有歧义,null是什么意思?错误?价值缺失?要么? null没有明确的含义,即使你给它赋予了含义,你也必须到处记录它并希望人们阅读它。 Value.EMPTY、Value.MISSING、throw new Exception() 都具有(某种程度上)明确定义的含义,可从您的代码中清楚地读取。这与将布尔值作为二进制枚举值的快捷方式的问题相同,例如:
val testPassed = runTest()
if (testPassed) // ...
这个意思很明确,只要有变量名就可以了。传递它、重构等可以很快混淆它。用户的意思:
val testResult = runTest()
if (testResult == TestResult.PASSED)
清晰易读。因此,根据相同的论点,让您的代码传达您的意图。不要走捷径。我再次认为 Kotlin 处理 null 非常好,对于处理糟糕的代码,我不认为它是产生糟糕代码的借口。
null在逻辑上是不合逻辑的,本质上它是一个不指向的指针。这基本上是一个废话 "value" 并不是真正的价值。这真的不是什么。
对于空值,您仍然需要为使用各种流功能的 API 做一些奇怪的事情,因此通过使用空值,您将不得不做一些棘手的事情,如争论 s1m0nw1 链接到,或者无论如何都进行显式转换,所以你最终会同时拥有它们,通过选择 null 完全没有节省任何东西,但最终处理了可为 nullable 和 Optional 的语义。
这是确保您 return 可选的最低工作量。我的意思是来一个,我们在这里不是在谈论很多额外的代码。不要害怕写一些额外的行来做正确的事情。可读性、灵活性等应该放在短代码之前。仅仅因为有一个新的智能功能可以快速编写内容并避免错误,并不意味着您必须一直使用它。请记住,对于锤子来说,一切看起来都像钉子:)
不执行 null 的问题是 Java 中缺少合适的 "Maybe" 类型。更正确的解决方案是函数式语言使用的 Option/Maybe 类型,但是 Java 的 Optional 只是一半的措施。它没有完全捕获真正 Maybe/Option 类型的语义,这就是为什么您不应该对参数和字段使用 Optional。
对于参数,这不是问题,因为您可以轻松确保不以该参数开头的重载 - 在 Kotlin 等语言中,这项任务甚至更容易。
对于字段,目前的 "solutions" 似乎要定义一个适当的 Maybe/Option 类型,可能特定于您的每种类型,以确保类型擦除和序列化不会妨碍您。您可以使用 NullObject 模式,这似乎是一种稍微丑陋的方式来完成完全相同的事情。或者有讨厌的 null 值,但将它完全封装在你的 class 中。到目前为止,我一直在做最后一个,但我不喜欢它:/但实用主义有它的一席之地;)