10

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.

smallufo
  • 11,516
  • 20
  • 73
  • 111
  • [An `Optional`’s place in Kotlin](https://medium.com/square-corner-blog/an-optionals-place-in-kotlin-17d7b271eefe) - when it's not required, nullable types are definitely the more idiomatic solution – Salem Nov 28 '17 at 10:50
  • 2
    In your example you don't even have to use the `takeIf` part. It will be the same without as well. – tynn Nov 28 '17 at 13:58
  • Thanks @tynn ... – smallufo Nov 28 '17 at 13:59

2 Answers2

7

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

s1m0nw1
  • 76,759
  • 17
  • 167
  • 196
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 ;)

Rohde Fischer
  • 1,248
  • 2
  • 10
  • 32
  • the Kotlin optionals are bad implemented in comparison to other languages. I think his suggestion above is better. – JBarros35 Sep 25 '20 at 15:49
  • @JBarros35 probably the cleanest would be to implement a specific data structure for the purpose and use meaningful worlds for it. After all "empty" or "null" can mean just about anything depending on the context, but if you have a structure named "NotSpecified" or "NotFound" it is much more clear. Question is if we want to do the needed amount of work, to get that in a good way? (No I do not have a good answer for that) – Rohde Fischer Sep 25 '20 at 17:40
  • 1
    Like with everything, there are varying opinions here, but some people contend that: "the concept of null is not mistake, but Java’s type-system, that considers null to be a member of every type, is". Source: https://elizarov.medium.com/null-is-your-friend-not-a-mistake-b63ff1751dd5 – Bennett Lynch Jul 27 '22 at 00:13