Compare Java 8 Optional with Swift

Overview

In this article, based on the items in the overview of Optional Apple Official Documentation, Swift's ʻOptional type and Java 8 ʻCompare Optional types. Although the content is a little refreshing now, I have posted it as a personal memorandum. End of article also includes a comparison table of each notation. Please look at them all together. The snippets in this article have been simplified a little, so some of them will not work even if you copy and paste them as they are. Please note.

Nil-Coalescing Operator

Swift

Unwrap ʻOptinalto return the contents, if any, or the specified default value otherwise. Use the operator??. You can also connect multiple ʻOptional types as operands.

Swift


let wrappedValue: String? = nil
let defaultValue = "default"
let value = wrappedValue ?? defaultValue
print(value)
// "default"

Swift


let wrappedValue: String? = nil
let defaultValue = "default"

let otherWrappedValue: String? = nil
//As long as the right operand is Optional??Can be connected with
let value = wrappedValue ?? otherWrappedValue ?? defaultValue
print(value)
// "default"

Java 8

In Java 8, the methods are ʻorElse and ʻorElseGet instead of operators. ʻOrElse passes the default value itself, and ʻorElseGet passes an object of typeSupplier <? Extends T>that provides the default value. However, both methods return a wrapped type, so you can't connect ** Optional types with ʻor Else like Swift **.

Java


Optional<String> wrappedValue = Optional.empty();
String defaultValue = "default";
String value = wrappedValue.orElse(defaultValue);
System.out.println(value);
// "default"

String otherValue = wrappedValue.orElseGet( () -> defaultValue );
System.out.println(otherValue);
// "default"

Java


Optional<String> otherWrappedValue = Optional.of("other");
//I can't do this
// String unwrappedValue = wrappedValue.orElse(otherWrappedValue).orElse(defaultValue);

Short-circuit evaluation

Swift

Swift's Nil join operator evaluates short-circuit. Therefore, the right operand will not be evaluated unless the left operand is nil.

Swift


func hoge() -> String {
    print("called")
    return "hoge"
}

let value = nil ?? hoge()
print(value)
// "called"
// "hoge"

//Since it is a short-circuit evaluation, hoge()Is not called
let value = "non nil" ?? hoge()
print(value)
// "non nil"

Java 8

Java 8's ʻor Else Get is also short-circuit evaluation, but ** ʻor Else does not **. It's natural because the default value itself is passed.

Java


public String hoge() {
    System.out.println("called");
    return "hoge";
}

String value = Optional.<String>empty().orElseGet(this::hoge);
System.out.println(value);
// "called"
// "hoge"

//Since it is a short-circuit evaluation, hoge()Is not called
String value = Optional.of("non null").orElseGet(this::hoge);
System.out.println(value);
// "non null"

Java


//orElse does not evaluate short circuit
String value = Optional.of("non null").orElse(hoge());
System.out.println(value);
// "called"
// "non null"

Regarding proper use, if it costs money to generate the default value, it is better to use ʻorElseGet` for short-circuit evaluation.

Java


public String fetchValue() {
    //Time-consuming process
    return result;
}

// △ -Be sure to fetchValue()Is called, so at least it affects performance
String value = wrappedValue.orElse(fetchValue());
// ◯ -Since it is a short-circuit evaluation, it will not be called if there is content
String value = wrappedValue.orElseGet(this::fetchValue);

Java


// △ -If there is content, it will be useless object generation
Person person = wrappedPerson.orElse(new Person());
// ◯ -Since it is a short-circuit evaluation, no object will be created if there is content
Person person = wrappedPerson.orElseGet(Person::new);

Unconditional Unwrapping

Swift

Unwraps the value with or without content. Use the operator !. If it is nil, it will cause a run-time error.

Swift


let number = Int("42")!
print(number)
// 42

Java 8

Java 8 uses the get method. If it is null, an unchecked exception NoSuchElementException is thrown.

Java


public Optional<Integer> intFromString(String string) {
    try {
        return Optional.of(Integer.parseInt(string));
    } catch (NumberFormatException ignored) {
        return Optional.empty();
    }
}

int number = intFromString("42").get();
System.out.println(number);
// 42

conversion

Swift

Convert the value. Use a method called map. If there is content, it will convert the value according to the given (Wrapped)-> U type closure, and if it is nil, it will flow as nil. Therefore, it returns the ʻOptional` type.

Swift


let intString: String? = "42"
let percentage: String? = intString.map { "\($0)%" }
print(String(describing: percentage))
// Optional("42%")

If you want to return a ʻOptional type in a closure, use the flatMapmethod that takes a closure of type(Wrapped)-> Optional ` as an argument.

Swift


let intString: String? = "42"
// Int(String)Is an int?return it
let integer: Int? = intString.flatMap { Int($0) }
print(String(describing: integer))
// Optional(42)

Java 8

Java 8 also uses the methods map and flatMap. The usage is the same.

Java


Optional<String> intString = Optional.of("42");
Optional<String> percentage = intString.map( str -> str + "%" );
System.out.println(percentage);
// Optional[42%]

Java


Optional<String> intString = Optional.of("42");
Optional<Integer> integer = intString.flatMap(this::intFromString);
System.out.println(integer);
// Optional[42]

Optional Binding

Swift

I think it is familiar as a safe unwrapping method. By combining with keywords such as ʻif and guard`, you can branch the process depending on whether there is content or not.

Swift


let wrappedValue: String? = "value"
if let value: String = wrappedValue {
    print(value)
} else {
    print("no value")
}
// "value"

Swift


let wrappedValue: String? = "value"
guard let value: String = wrappedValue else {
    print("no value")
    return
}
print(value)
// "value"

Java 8

In Java 8, you can safely unwrap it via a method called ʻifPresent. It takes an object of type Consumer <? Super T>` as an argument and describes the processing when there is a content in it.

Java


Optional<String> wrappedValue = Optional.of("value");
wrappedValue.ifPresent( str -> {
    System.out.println(str);
});
// "value"

However, there is no ** method that can describe the processing when there is no content **. Therefore, if you want to describe both processes, you need to use ʻis Presentto check the existence of the contents. However, it is redundant compared to ** Swift because you have to explicitly describe the forced unwrapget` if there is content **.

Java


Optional<String> wrappedValue = Optional.of("value");
if (wrappedValue.isPresent()) {
    //Redundant but must be explicitly forced unwrapped
    String value = wrappedValue.get();
    System.out.println(value);
} else {
    System.out.println("no value");
}
// "value"

Java


Optional<String> wrappedValue = Optional.of("value");
//Describe in card clause
if (!wrappedValue.isPresent()) {
    System.out.println("no value");
    return;
}
//Redundant but must be explicitly forced unwrapped
String value = wrappedValue.get();
System.out.println(value);
// "value"

Java


Optional<String> wrappedValue = Optional.of("value");
wrappedValue.ifPresent( str -> {
    System.out.println(str);
})// .orElse( () -> { //I can't do this
//     System.out.println("no value");
// })
;

Also, since the scope of the process passed to ʻifPresent is within the lambda expression, the calling method cannot be returned within that process. Therefore, if you want to make an early return, you have no choice but to use ʻis Present.

Java


public boolean foo(Optional<String> wrappedValue) {
    //Return early if there is a value
    if (wrappedValue.isPresent) {
        String str = wrappedValue.get();
        //Do something
        return true;
    }
    //What to do if there is no value
    return result;
}

Java


public boolean foo(Optional<String> wrappedValue) {
    //Return early if there is a value
    wrappedValue.ifPresent( str -> {
        //Do something
//         return true; //This is not possible because the scope is in this lambda expression
    });
    //What to do if there is no value
    return result;
}

Optional Chaining

Swift

Access properties and methods only when there is content. Use the postfix operator ?.

Swift


let wrappedValue: String? = "value"
let uppercased: String? = wrappedValue?.uppercased()
print(String(describing: uppercased))
// Optional("VALUE")

Swift


class Hoge {
    func hoge() { print("hoge") }
}

let wrappedValue: Hoge? = Hoge()
wrappedValue?.hoge()
// "hoge"

Java 8

In Java 8, you can use the map method introduced earlier. If the target is a method, you can write it concisely using method references. However, if the target returns a ʻOptional type, you must use flatMap`.

Java


Optional<String> wrappedValue = Optional.of("value");
Optional<String> uppercased = wrappedValue.map(String::toUpperCase);
System.out.println(uppercased);
// Optional[VALUE]

Java


class User {
    final String id;
    Optional<String> mail = Optional.empty();
    User(String id) { this.id = id; }
}

Optional<User> wrappedUser = Optional.of(new User("user1"));
Optional<String> mail = wrappedUser.flatMap( user -> user.mail );
System.out.println(mail);
// Optional.empty

It is exactly the same notation as when using Convert, including how to use it properly. Java 8 does not provide any special notation or method just to access the field or method of the wrapped value. To access a method that does not return a value, use ʻifPresent` introduced earlier.

Java


class Hoge {
    void hoge() { System.out.println("hoge"); }
}

Optional<Hoge> wrappedValue = Optional.of(new Hoge());
wrappedValue.ifPresent(Hoge::hoge);
// "hoge"

Summary

In this article, we compared Java 8 and ʻOptional types based on the handling of ʻOptional types in Swift. Below is a comparison table of each notation. Especially when it comes to optional binding and optional chaining, the difference between the two is very apparent. Java 8 optional binding is a bit more verbose than Swift because it can't be unwrapped at the same time as branching. Also, Java 8 doesn't provide a dedicated way to access wrapped values like the Swift ? Operator.

Swift Java
Nil join operator ?? orElse* Non-short circuit evaluation
orElseGet
Forced unwrap ! get
conversion map
flatMap
map
flatMap
Optional binding if let
guard let
ifPresent
OrisPresentAfter branching atgetForced unwrap with
Optional chaining ? OptionalIf you want to return the typeflatMap
If you return anything elsemap
If it does not return a valueifPresent

If you have any mistakes, please let us know in the comments.

Bonus-Java 9 Optional

Here we introduce the ʻOptional methods, ʻor and ʻifPresentOrElse`, added in Java 9. I can't talk about Java 8. It's a complete bonus.

or

Executes an object of type Supplier <? extends Optional <? extends T >> given when it is empty to get the default value of type ʻOptional. This corresponds to just bringing the ʻOptional type to the right operand in Swift's Nil join operator. Also, because of ** short-circuit evaluation **, the given Supplier object will not be executed as long as it has contents.

Swift


let wrappedValue: String? = nil
let defaultValue = "default"

let otherWrappedValue: String? = nil
let value = wrappedValue ?? otherWrappedValue ?? defaultValue
print(value)
// "default"

Java


// available Java 9 or later
Optional<String> wrappedValue = Optional.empty();
String defaultValue = "default";

Optional<String> otherWrappedValue = Optional.empty();
//Optional type can be specified as the default value in Java 9 or later
String value = wrappedValue.or( () -> otherWrappedValue ).orElse(defaultValue);
System.out.println(value);
// "default"

ifPresentOrElse

You can branch the process depending on whether it has contents or not. If there is content, an object of type Consumer <? Super T> will be executed, otherwise an object of type Runnable will be executed. Since you can also describe the processing when it is null, it is much closer to Swift's optional binding compared to Java 8.

Swift


let wrappedValue: String? = "value"
if let value = wrappedValue {
    print(value)
} else {
    print("no value")
}
// "value"

Java


// available Java 9 or later
Optional<String> wrappedValue = Optional.<String>empty();
wrappedValue.ifPresentOrElse( str -> {
    System.out.println(str);
}, () -> {
    System.out.println("no value");
});
// "no value"

Recommended Posts

Compare Java 8 Optional with Swift
Rewrite Java try-catch with Optional
[Java] How to compare with equals method
Compare Integer with ==
24 hours struggling with Android development, Java Optional
[Java] Optional memorandum
Java 9 Optional :: stream
[Swift] Perform error handling with Optional <Wrapped> type
Compare objects in Swift
Install java with Homebrew
Install Java with Ansible
Comfortable download with JAVA
View Java Optional Javadoc
Switch java with direnv
Research on Swift Optional
Compare Lists in Java
Getting Started with Swift
Download Java with Ansible
Let's scrape with Java! !!
Starting with Swift Swift UI
Build Java with Wercker
Endian conversion with JAVA
Easy BDD with (Java) Spectrum?
Use Lambda Layers with Java
Getting Started with Java Collection
Java Config with Spring MVC
Basic Authentication with Java 11 HttpClient
Let's experiment with Java inlining
Run batch with docker-compose with Java batch
[Template] MySQL connection with Java
Install Java 7 with Homebrew (cask)
[Java] JSON communication with jackson
Java to play with Function
Try DB connection with Java
How to handle exceptions coolly with Java 8 Stream or Optional
How to use java Optional
Enable Java EE with NetBeans 9
[Java] JavaConfig with Static InnerClass
Try gRPC with Java, Maven
Let's operate Excel with Java! !!
Version control Java with SDKMAN
RSA encryption / decryption with java 8
Progress made with Swift UI
Paging PDF with Java + PDFBox.jar
Sort strings functionally with java
Object-oriented (java) with Strike Gundam
[Java] How to use Optional ②
[Java] Content acquisition with HttpCliient
Java version control with jenv
Troubleshooting with Java Flight Recorder
Streamline Java testing with Spock
Connect to DB with Java
Connect to MySQL 8 with Java
Using Mapper with Java (Spring)
Java study memo 2 with Progate
[Swift] I thought about compare
Getting Started with Java Basics
Use SpatiaLite with Java / JDBC
[Java] How to use Optional ①
Be sure to compare the result of Java compareTo with 0
Study Java with Progate Note 1