Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should Kotlin's DAO return Optional or null?

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.

like image 668
smallufo Avatar asked Nov 28 '17 10:11

smallufo


People also ask

Should we use optional in Kotlin?

Depending on whom you ask, using Optional as the type of a field or return type is bad practice in Java. Fortunately, in Kotlin there is no arguing about it. Using nullable types in properties or as the return type of functions is considered perfectly valid and idiomatic in Kotlin.

How do I return a null object in Kotlin?

In Kotlin, there are optional types. If you return ServiceChargeMasterList, you say to the compiler that you will never return null. If you want to return null, you have to add ? sign at the end of the type which indicates that you can return an instance of ServiceChargeMasterList or null.

How do you handle null in Kotlin?

You can use the "?. let" operator in Kotlin to check if the value of a variable is NULL. It can only be used when we are sure that we are refereeing to a non-NULL able value. The following example demonstrates how this operator works.


2 Answers

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

like image 148
s1m0nw1 Avatar answered Sep 20 '22 02:09

s1m0nw1


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 ;)

like image 20
Rohde Fischer Avatar answered Sep 21 '22 02:09

Rohde Fischer