Kotlin optional to nullable

Java Optional and Kotlin Nullable

We are using Kotlin and Spring Boot in one of our projects. This includes Spring Boot Jpa Repositories. We are using a CrudRepository that provides basic functionalities like save(), delete(), findById() etc. This library is written in Java, hence method signatures look like this:

Using this API out of the box would force us to deal with cumbersome Java Optionals. Since we are implementing in Kotlin we would like to use Kotlin Nullables instead and as it turned out we can achieve this very easily.

This is where extensions can help a lot. I already knew the concept of extensions from Swift and found that Kotlin also supports extensions. They enable us to add functionality to classes that we cannot change or subclass, for example java.util.Optional as it is final.

So all I need is a simple piece of code that converts a Java Optional into a Kotlin Nullable. Ideally this should be available on any Java Optional object (in our project only ). We can achieve this by simply extending the Java Optional class as follows:

fun Optional.toNullable(): T? = this.orElse(null)

I created the following test to verify that the extension works as intended:

@Test fun `can convert Java Optional to Kotlin Nullable`() < val optionalString = Optional.of("any String") val nullableString: String? = optionalString.toNullable() assertThat(nullableString).isEqualTo("any String") val emptyOptionalString: Optional= Optional.empty() val emptyNullableString: String? = emptyOptionalString.toNullable() assertThat(emptyNullableString).isNull() >

Now we can wrap the call to the repository into a service that returns a Kotlin Nullable instead of a Java Optional as follows:

@Service class MyService constructor(@Autowired val myRepository: MyRepository)

Now we can use all the handy things that come with Kotlin Nullables, like Elvis, better readability and so on.

Читайте также:  Classes appear in java lang

Topics

Red Hat JBoss Permier Business Partner

  • All (184)
    • Data Science (5)
    • DevOps (10)
    • Enterprise Applications (86)
    • Functional Programming (11)
      • haskell (3)
    • Industrie 4.0 (1)
    • Microsoft Azure (2)
    • Mobile Solutions (38)
    • Playground (32)
    • Project Management (3)
    • Security (3)
    • SpringBoot (2)
    • User Experience (9)

Recent Posts

Источник

Java Optionals and Kotlin Nulls

When Java 8 introduced Streams for operating on collections of data, it also introduced a similar concept, Optional, which has many methods that are similar to Stream , but operates on a single value that might or might not be present.

As you migrate your projects from Java to Kotlin, you might come across some Optional objects. What should you do? Should you leave them as Optional , or change them to more idiomatic Kotlin?

In this guide, we’ll take a look at how Kotlin’s null-safety features can be used to replace Java 8’s Optional class. Then we’ll wrap up with a few more thoughts about the trade-offs between using Optional and using Kotlin with nullable types.

To explore all of the possibilities, we’ll assume these constants:

static Optional present = Optional.of("Hello"); static Optional absent = Optional.empty(); 
val present: String? = "Hello" val absent: String? = null 

Creation methods

empty()

The Kotlin equivalent of assigning an empty() is to assign a null .

Optional s1 = Optional.empty(); 

of()

The of() method creates an Optional if a value is present, or forces an immediate NullPointerException otherwise. In Kotlin, we have to go out of our way to throw the exception.

Optional s2 = Optional.of("Hello"); Optional s3 = Optional.of(null); 
val s2: String? = "Hello" val s3: String? = null ?: throw NullPointerException() 

ofNullable()

The ofNullable() method works the same as of() , except that instead of throwing a NullPointerException , a null value produces an empty . The Kotlin equivalent is straightforward.

Optional s4 = Optional.ofNullable("Hello"); Optional s5 = Optional.ofNullable(null); 
val s4: String? = "Hello" val s5: String? = null 

Transformation methods

map()

The map() method allows you to transform an Optional to an Optional of another value. The equivalent in Kotlin is let() . Since our example uses a nullable String , be careful not to accidentally use the extension function CharSequence.map() , which will map each character of the String instead of the String itself.

present.map(it -> "The value is " + it); // Optional[The value is Hello] present.map(it -> null); // Optional.empty absent.map(it -> "The value is " + it); // Optional.empty absent.map(it -> null); // Optional.empty 
present?.let < "The value is $it" >// The value is Hello present?.let < null >// null absent?.let < "The value is $it" >// null absent?.let < null >// null 

In some cases, it’s not necessary to call let() at all. This happens when you’re mapping to a property or function on the nullable variable. For example:

present.map(String::length); // Optional[5] present.map(it -> it.toUpperCase()); // HELLO 
present?.length // 5 present?.toUpperCase() // HELLO 

flatMap()

When mapping an Optional in Java, sometimes you have to unwrap another Optional . To do this, you use flatMap() instead of map() . With Kotlin’s null system, the value is either present, or null , so there’s nothing to unwrap. This means that Kotlin’s equivalent for flatMap() and map() are similar.

Notice that you have to go out of your way to throw a NullPointerException in Kotlin — otherwise, the third example below would have just returned a null .

(For this example, we’ll introduce a third optional value, other ).

Optional other = Optional.of("World"); present.flatMap(it -> other); // Optional[World] present.flatMap(it -> absent); // Optional.empty present.flatMap(it -> null); // throws NullPointerException absent.flatMap(it -> other); // Optional.empty absent.flatMap(it -> absent); // Optional.empty absent.flatMap(it -> null); // Optional.empty 
val other: String? = "World" present?.let < other >// World present?.let < absent >// null present?.let < null >?: throw NullPointerException() // throws NullPointerException absent?.let < other >// null absent?.let < absent >// null absent?.let < null >// null 

filter()

The filter() method transforms a value to an empty() if a predicate isn’t matched. The Kotlin way of filtering a nullable value is to use takeIf() .

present.filter(it -> it.startsWith("H")); // Optional[Hello] present.filter(it -> it.startsWith("T")); // Optional.empty absent.filter(it -> it.startsWith("H")); // Optional.empty absent.filter(it -> it.startsWith("T")); // Optional.empty 
present?.takeIf < it.startsWith("H") >// "Hello" present?.takeIf < it.startsWith("T") >// null absent?.takeIf < it.startsWith("H") >// null absent?.takeIf < it.startsWith("T") >// null 

Kotlin gets bonus points for allowing you to invert the predicate with takeUnless() .

present?.takeUnless < it.startsWith("H") >// null present?.takeUnless < it.startsWith("T") >// Hello absent?.takeUnless < it.startsWith("H") >// null absent?.takeUnless < it.startsWith("T") >// null 

Conditional methods

ifPresent()

The safe call operator ?. in Kotlin allows you to call a function on the value if the value is not null . To make this work similarly to the Consumer that ifPresent() accepts, we’ll use the .also() extension method supplied by the standard library since Kotlin 1.1.

present.ifPresent(it -> < System.out.println("The value is " + it); >); absent.ifPresent(it -> < System.out.println("The value is " + it); >); 

isPresent()

Testing for the presence of a value in Kotlin is as easy as comparing it to null .

if (present.isPresent()) < System.out.println("It's present"); >if (!absent.isPresent())
if (present != null) < println("It's present") >if (absent == null)

Unwrapping methods

get()

Since nullable types in Kotlin are not wrapped in another class like Optional , there’s no need for an equivalent of the get() method — just assign the value where you need it.

Again, we have to go out of our way to throw an exception to match the Java API.

String x = present.get(); String y = absent.get(); 
val x = present val y = absent ?: throw NoSuchElementException() 

orElse()

To provide a default to use when the value is null , use the safe call operator. Notice that the equivalent of orElse(null) is simply to evaluate the value — using the safe call operator in those cases is redundant.

present.orElse("Greetings"); // Hello present.orElse(null); // Hello absent.orElse("Greetings"); // Greetings absent.orElse(null); // null 
present ?: "Greetings" // Hello present // Hello absent ?: "Greetings" // Greetings absent // null 

orElseGet()

Sometimes you want a default that isn’t a literal. This is especially handy when the default is expensive to compute — you only want to take that performance hit if the Optional is empty . That’s why Java’s Optional provides orElseGet() .

In Kotlin, the expression to the right of the safe call operator is only evaluated if the left-hand side is null , so our Kotlin approach looks similar to the previous code listing.

String getDefaultGreeting(boolean value) < return value ? "Greetings" : null; >present.orElseGet(() -> getDefaultGreeting(true)); // Hello present.orElseGet(() -> getDefaultGreeting(false)); // Hello absent.orElseGet(() -> getDefaultGreeting(true)); // Greetings absent.orElseGet(() -> getDefaultGreeting(false)); // null 
fun getDefaultGreeting(value: Boolean) = if (value) "Greetings" else null present ?: getDefaultGreeting(true) // Hello present ?: getDefaultGreeting(false) // Hello absent ?: getDefaultGreeting(true) // Greetings absent ?: getDefaultGreeting(false) // null 

orElseThrow()

Since throw is an expression in Kotlin (that is, it can be evaluated), we can use the safe call operator again here.

present.orElseThrow(RuntimeException::new); // Hello absent.orElseThrow(RuntimeException::new); // throws RuntimeException 
present ?: throw RuntimeException() // Hello absent ?: throw RuntimeException() // throws RuntimeException 

Trade-offs

Kotlin’s nullable types have many distinct advantages over Optional .

  • Because they aren’t wrapped in a class, getting at the actual value is easy.
  • Because they aren’t wrapped in a class, you can’t inadvertently assign a null to an Optional reference when you intended to assign an empty . For example, Optional name = null; instead of Optional name = Optional.empty();
  • The Kotlin expressions are generally more terse than their Java Optional counterparts, except in the cases where we wanted to throw an exception.

There are a few cases when you might still want to use an Optional in Kotlin:

  • You’re using a library with a function that expects an Optional argument or returns it as a result.
  • You’re using a library that doesn’t support null , such as RxJava 2. 1

Alternatives

  • Kategory, a functional programming library, includes an Option datatype that looks similar to Scala, using an instance of Some or None to represent the value’s presence. 2
  • If you’re looking for a lightweight alternative, there’s also Koptional. 1
  1. Thanks to Artem Zinnatullin for pointing this out. [return]
  2. Thanks to Raúl Raja for pointing this out. [return]

© 2017-2023 | Code snippets are licensed under Apache 2 unless otherwise noted. | Disclaimer | Privacy Policy | News Archives let expires = new Date(0).toGMTString(); document.cookie = «_jsuid=; expires=» + expires + «; path=/; domain=.typealias.com;» document.cookie = «cookieconsent_status=; expires Clicky» width=»1″ height=»1″ src=»//in.getclicky.com/101118828ns.gif»/>

Источник

Оцените статью