import java.util.Optional
import static extension de.fhg.fokus.xtensions.optional.OptionalExtensions.*
// ...
val Optional<String> no = none
val Optional<String> yes = some("yesss!")
val Optional<String> dunno = maybe(possiblyNull())
// ...
private def String possiblyNull() {
if(System.currentTimeMillis % 2 == 0) {
"I'm in ur optional"
} else {
null
}
}
Extensions to Optional
The static factory Methods for creating Optional instances are not really meant to be used as
statically imported methods. They have no meaningful names to be used this way. They also differ from
the commonly used names some, none and maybe which are used in many other languages.
The class OptionalIntExtensions provides static factory methods with these common names
which are implemented to be inlined to the factory methods used by the JDK.
Examples:
The optional class does not provide a filter method, that filters the optional based on the class
the wrapped object is instance of, as known e.g. from Xtend’s filter methods on Iterable.
The OptionalIntExtensions adds such a method, providing an instance check of the wrapped value.
Example:
import java.util.Optional
import static extension de.fhg.fokus.xtensions.optional.OptionalExtensions.*
// ...
val Optional<Object> optObj = some("Hi there!")
val Optional<String> optStr = optObj.filter(String)
optStr.ifPresent [
println(it.toUpperCase)
]
When testing an Optional for a value or otherwise perform a different operation
the optional has to be used twice e.g.
import java.util.Optional
// ...
val noVal = Optional.empty
if(noVal.isPresent) {
val value = noVal.get
println("Here is your value: "+ value)
} else {
println("Awww, no value")
}
This can be error prone, since the optional (in the example noVal) has to be
used twice and a different optional may be used accidently. To not run into this
issue this library provides the whenPresent method which allows defining an
else branch on the returned object.
Example:
import java.util.Optional
import static extension de.fhg.fokus.xtensions.optional.OptionalExtensions.*
// ...
val noVal = Optional.empty
noVal.whenPresent [
println("Here is your value: "+ it)
].elseDo [
println("Awww, no value")
]
Alternatively, the ifPresentOrElse extension method can be used, but this does not
have a clear visual separation which case is the if and which the else callback.
To avoid allocating objects over and over for the lambda passed to the
elseDo method, there are overloaded versions of the method passing on
additional parameters to the lambda. This can avoid "capturing" lambdas
which would create a new object on every elseDo call.
Example:
import java.util.Optional
import static extension de.fhg.fokus.xtensions.optional.OptionalExtensions.*
// ...
val captureMe = "no value"
val noVal = Optional.empty
noVal.whenPresent [
println("Here is your value: "+ it)
].elseDo(captureMe) [
println("Awww, " + it)
]
To bridge between APIs providing an Optional value and ones that expect
multiple values, the extension methods asIterable, toList and toSet
are provided to create immutable implementations of common JVM collection APIs.
The Optional class has a map method that can map the value present in the optional
to a value of another type. Unfortunately there is no method to map to a primitive type
returning a primitive optional, such as OptionalInt. The extension methods mapInt,
mapLong, and mapDouble allow mapping to primitive options without having to
box the resulting value. To map to a boolean value the method test can be used,
which returns an OptionalBoolean. This extension method is mostly intended to check
a property on a value wrapped in an Optional.
Example:
import java.util.Optional
import static extension de.fhg.fokus.xtensions.optional.OptionalExtensions.*
// ...
val Optional<String> yes = some("yesss!")
val OptionalInt lenOpt = yes.mapInt[length]
val len = lenOpt.orElse(0)
println("Length is " + len)
The call chain map[…].orElseGet[..] is pretty common. As a shortcut the method mapOrGet
is provided.
Some methods on Optional introduced in Java 9 are available as retrofitted extension methods. When compiling a class using the extension method targeting Java 9, the native Optional method has precedence and will be used. No changes in the source code has to be done to switch to the native Java 9 implementation. The following instance methods of Optional are backported for Java 8:
As a shortcut for the or extension method, the || operator is provided. The ?: operator is a shortcut for the orElse method on Optional.
|
Tip
|
Related JavaDocs: |
Extensions to Primitive Optionals
Extensions to the primitive versions of Optional are provided by the following classes:
de.fhg.fokus.xtensions.optional.OptionalIntExtensions de.fhg.fokus.xtensions.optional.OptionalLongExtensions de.fhg.fokus.xtensions.optional.OptionalDoubleExtensions
Same as for Optional, there is a some alias for the OptionalInt.of, OptionalLong.of, and OptionalDouble.of
methods (see Extensions to Optional).
The methods noInt, noLong, and noDouble provide empty primitive Optionals.
The Open JDK / Oracle JDK currently does not cache OptionalInt and OptionalLong instances in the static factory method
OptionalInt.of(int) and OptionalLong.of(long) as it is currently done for Integer creation in
Integer.valueOf(int). To provide such a caching static factory methods, the
OptionalIntExtensions.someOf(int) and OptionalLongExtensions.someOf(long) method were
introduced.
Example:
import static de.fhg.fokus.xtensions.optional.OptionalIntExtensions.*
// ...
if(someOf(42) === someOf(42)) {
println("someOf caches instances")
}
Stunningly, the primitive versions of Optional do not provide map and filter methods. These
are provided as extension methods by this library.
OptionalBoolean
The class de.fhg.fokus.xtensions.optional.OptionalBoolean represents a
boolean value that may be absent. As with other optional types,
references to objects of this type should never be null! It is mostly intended to be
used as a return value from methods to allow for fluent call chains.
Creation
The static factory method empty creates an OptionalBoolean not holding a value.
Creating an OptionalBoolean from a Boolean that may be null, the ofNullable
factory method can be used; an alias meant to be used as an extension method is the asOptional
factory method. To create an OptionalBoolean from a boolean value, definitely holding
a value, the static of factory method can be used. The static methods ofTrue and ofFalse
provide OptionalBoolean wrapping true or false.
Testing for Content
To check if an OptionalBoolean is either empty, wrap true or wrap false, the class
provides several methods:
-
boolean isEmpty() -
boolean isPresent() -
boolean isTrue() -
boolean isTrueOrEmpty() -
boolean isFalse() -
boolean isFalseOrEmpty()
Note that isTrue and isFalse return false if the OptionalBoolean is empty.
Analogous to the methods mentioned above there are methods that test for a value and execute a
given callback if the condition applies. These methods have the if prefix, e.g. void ifTrue(()⇒void then).
Additional the void ifPresentOrElse(BooleanConsumer action, Runnable emptyAction) method
takes two callbacks, one for the case of an optional with value, and one that is invoked if the optional is empty.
import de.fhg.fokus.xtensions.optional.OptionalBoolean
// ...
val random = new Random()
val randomBool = if (random.nextBoolean) {
OptionalBoolean.empty
} else {
OptionalBoolean.of(random.nextBoolean)
}
// will not print on empty optional
randomBool.ifTrue [
println("Yippie! A random true!")
]
Extracting Values
There are several methods that allow to extract a wrapped value, and handle the case of an empty optional in different ways.
-
orElse(boolean fallback)returns a given default value on empty, -
orElseGet(BooleanSupplier fallback)computes a default value (viafallback) on empty -
<X extends Throwable> boolean orElseThrow(Supplier<? extends X> exceptionSupplier) throws Xthrows an exception provided byexceptionSupplieron empty
Note that the OptionalBoolean does not have a simple get method which throws an
NoSuchElementException as other optionals have. However the method getNullable
returns a Boolean which is null if the optional is empty.
Example:
import de.fhg.fokus.xtensions.optional.OptionalBoolean
import static extension de.fhg.fokus.xtensions.optional.OptionalExtensions.*
// ...
val names = #["Mike", "Joe", "Christian"]
val longNameWithC = names.stream
.filter[length > 4]
.findFirst
.test[startsWith("C")]
.orElse(false)
if(longNameWithC) {
println("The long name starts with 'C'")
}
Note that the test method is an extension method on Optional provided by OptionalExtensions
returning an OptionalBoolean.
|
Tip
|
Related JavaDocs: |