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.
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);
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);
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.
returns the ʻOptional <Integer>
type for convenience of explanation, but it is usually better to use the ʻOptionalInt` type when wrapping an integer.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
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]
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 unwrap
get` 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;
}
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"
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 evaluationorElseGet |
Forced unwrap | ! |
get |
conversion | map flatMap |
map flatMap |
Optional binding | if let guard let |
ifPresent Or isPresent After branching atget Forced unwrap with |
Optional chaining | ? |
Optional If you want to return the typeflatMap If you return anything else map If it does not return a value ifPresent |
If you have any mistakes, please let us know in the comments.
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