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 X
throws an exception provided byexceptionSupplier
on 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: |