Should Kotlin's DAO return Optional or null?

痞子三分冷 提交于 2020-04-12 19:46:40

问题


Prior to Kotlin/JPA , I used to write my DAO layer like this :

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

And in the caller side , if I want to find someone or create user by SSN , I can write this:

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

It works well and looks fluent.

But since Kotlin introduces null-safety , there is another idiomatic way (dao still in 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)
}

And in the client side :

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

Both ways work good. But I wonder which is preferred ? Is it good for Kotlin to dependent on Java8's Optional in API design ? What's the cons for a Kotlin project to depend on (or intercommunicate via) Java8's Optional/Stream API (since Kotlin has its own) ?

Kotlin can compile to JavaScript (I haven't studied that). If the project is depend on Java's Optional/Stream , will it have problem compiling to JS?

---- updated ----

According to Jetbrains

No, common code can only depend on other common libraries. Kotlin has no support for translating Java bytecode into JS.


回答1:


I wouldn't use Optional if you don't have to. It only adds unnecessary overhead as working with nullable types is more readable and more idiomatic in Kotlin. There's no advantage of using Optional in Kotlin.

Here's another discussion on that: https://discuss.kotlinlang.org/t/java-api-design-for-kotlin-consumption-optional-or-null/2455




回答2:


I have to say I disagree with @s1m0nw1

What Kotlin does is to give us a nice way of dealing with a mistake. Kotlin cannot remove that mistake, because it's so inherent in the JVM and would damage the integration with legacy Java and poorly written Java. However, because it has a nice tool to deal with the bad design does not mean that we should embrace the bad design and do it ourselves.

Some reasoning:

  • Nullable still give the same set of problems the second your Kotlin code is called from Java. Now you just masked the underlying problem, actively damaging your clients. - By itself this is a strong enough reason IMO.

  • Null is still ambiguous, what does null mean? Error? Value missing? Or? Null has no clear meaning, even if you assign it a meaning, you have to document it everywhere and hope that people reads it. Value.EMPTY, Value.MISSING, throw new Exception() all have (somewhat) clearly defined meanings clearly readable from your code. It's the same problem as with booleans as a shortcut for binary enum values, e.g.:

val testPassed = runTest()

if (testPassed) // ...

this has a clear meaning, only as long a you have the variable name. Passing it on, refactoring etc. can quite fast obfuscate it. What the user meant:

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

is clear and readable. So per the same argument, let your code communicate your intent. Do not take the shortcut. Again I see the Kotlin dealings with null as extremely nice, for dealing with poor code, I do not see it as an excuse for producing poor code.

  • Null is logically illogical, essentially it's a pointer that doesn't point. It's basically a nonsense "value" that isn't really a value. It's not really anything.

  • With nulls you will still have to do weird things for API's that utilize various stream functionality, so by using null you will either have to do hacky things like illustrated in the debate that s1m0nw1 links to, or do explicit conversions anyway, so you'll end up having them both anyway, saving exactly nothing by choosing null, but ending up dealing with both the semantics of the nullable and Optional.

  • It's a minimum amount work to ensure that you return Optional. I mean come one, we are not talking about a lot of extra code here. Don't be afraid of writing a few extra lines to do things right. Readability, flexibility etc. should come before short code. Just because there's a new smart feature to write things shortly and avoid mistakes does not mean you have to use it all the time. Keep in mind that to a hammer everything looks like a nail :)

The problem with not doing null though is the lack of a proper "Maybe"-type in Java. The more correct solution is the Option/Maybe types utilized by functional languages, however, Java's Optional is only a half measure. It does not fully capture the semantics of a true Maybe/Option type, which is why you shouldn't use Optional for parameters and fields.

For parameters this isn't an issue, since you can easily ensure an overload that doesn't take that parameter to begin with - a task that's even easier in languages like Kotlin.

For fields it seems the current "solutions" are to define a proper Maybe/Option-type, perhaps specific for each of your types, to ensure that type erasure and serializations doesn't hinder you. You could use the NullObject pattern, which seems like a slightly uglier way to do the exact same. Or have the nasty null value, but encapsulate it completely in your class. So far I've been doing the last, but I'm not fond of it :/ but pragmatism has its place ;)



来源:https://stackoverflow.com/questions/47529644/should-kotlins-dao-return-optional-or-null

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!