Maybe - A Better Optional
Why Maybe
Maybe
is an alternative to Java's Optional
. Java has Optional
then why do we need Maybe
?
- First of all, Java's
Optional
is notSerializable
. When it was added to Java 8, the designers of the language didn't want us to use it as a type for the object member variables. - Java's
Optional
has theget()
method which means it may throw an exception ifOptional.isPresent()
isfalse
.
Create
Maybe.maybe()
To construct Maybe
, use Maybe.maybe
. If the given argument is null
, it creates Nothing
, otherwise it's Just
with the given value.
import j8plus.types.Maybe;
Maybe.maybe(123);
// Maybe<Integer> = Just(123)
Maybe.maybe(null);
// Maybe = Nothing
Maybe.nothing()
import j8plus.types.Maybe;
Maybe.nothing();
// Maybe = Nothing
Maybe
from Optional
import j8plus.types.Maybe;
final Optional<String> optionalName = Optional.ofNullable("Kevin");
// Optional[Kevin]
// ...
final Maybe<String> maybeName = Maybe.fromOptional(optionalName);
// Maybe<String> = Just(Kevin)
Maybe
to Optional
If you need to convert Maybe
into Optional
, you can do it with Maybe.toOptional()
.
import j8plus.types.Maybe;
Maybe.maybe(1).toOptional(); // Optional[1]
Maybe.nothing().toOptional(); // Optional.empty
Transform
Maybe.map()
import j8plus.types.Maybe;
Maybe.maybe(1).map(n -> n * 2); // Maybe<Integer> = Just(2)
Maybe.flatMap()
import j8plus.types.Maybe;
public Maybe<User> findUser(final Long id)
// ...
final Maybe<User> maybeUser = findUser(1L);
// Maybe<User> = Just(User(1L, Kevin, Maybe<String> = Just(kevin@blah.blah)))
maybeUser.flatMap(user -> user.getEmail()); // Maybe<String> = Just(kevin@blah.blah)
final Maybe<User> maybeUser2 = findUser(2L);
// Maybe<User> = Just(User(2L, John, Maybe = Nothing))
maybeUser2.flatMap(user -> user.getEmail()); // Maybe = Nothing
Maybe.filter()
Maybe.filter()
can check if the value in Maybe.Just
matches the given Predicate
and if so, it keeps Maybe.Just
as is. If not, it turns it into Maybe.Nothing
.
If the Maybe
is already Maybe.Nothing
, nothing happens, and it is still Maybe.Nothing
.
import j8plus.types.Maybe;
Maybe.maybe(10).filter(n -> n > 9);
// Maybe<Integer> = Just(10)
Maybe.maybe(8).filter(n -> n > 9);
// Maybe = Nothing
Get the Value
Maybe.fold()
So if Optional.get()
is dangerous because it throws NoSuchElementException
when Optional
has a null
value (i.e. Optional.empty()
), what should I use?
A better way is using fold
which is also called as 'Catemorphism' in functional programming.
Maybe.fold()
takes two parameters. The first one is a Supplier
to get the alternative value when Maybe
is Nothing
. The other one is a Function
to handle the value when it is Maybe.Just
.
e.g.)
import j8plus.types.Maybe;
Maybe.maybe(10)
.fold(
// Nothing case: When it's Nothing, returns 0.
() -> 0,
// Just case: When it's Just, return the value after applying this function.
n -> n * 5
); // 50
import j8plus.types.Maybe;
Maybe.maybe(1).fold(() -> 0, n -> n * 2); // 2
Maybe.nothing().fold(() -> 0, n -> n * 2); // 0
Maybe.maybe("Kevin").fold(() -> "", name -> "Hello " + name);
// "Hello Kevin"
Maybe.nothing().fold(() -> "", name -> "Hello " + name);
// ""
When it is Maybe.Just
, if you don't want to change the value but just want to get it, you can use Function.identity()
for the Just
case function.
import java.util.function.Function;
import j8plus.types.Maybe;
Maybe.maybe(123).fold(() -> 0, Function.identity()); // 123
Maybe.nothing().fold(() -> 0, Function.identity()); // 0
Maybe.fold()
is the same as Maybe.map().getOrElse()
.
import j8plus.types.Maybe;
Maybe.maybe(1).fold(() -> 0, n -> n * 2); // 2
Maybe.maybe(1).map(n -> n * 2).getOrElse(() -> 0); // 2
Maybe.nothing().fold(() -> 0, n -> n * 2); // 0
Maybe.nothing().map(n -> n * 2).getOrElse(() -> 0); // 0
Maybe.getOrElse()
If you don't need to change the value in the Maybe
and just want to get it, you can use Maybe.getOrElse()
.
import j8plus.types.Maybe;
Maybe.maybe(1).getOrElse(() -> 0); // 1
Maybe.nothing().getOrElse(() -> 0); // 0
Check Maybe Type
Maybe.isJust()
To check if a Maybe
instance is Maybe.Just
, use Maybe.isJust()
. If true
, it is Maybe.Just
. If false
, it is Maybe.Nothing
.
import j8plus.types.Maybe;
Maybe.maybe(1).isJust(); // true
Maybe.nothing().isJust(); // false
Maybe.isNothing()
To check if a Maybe
instance is Maybe.Nothing
, use Maybe.isNothing()
. If true
, it is Maybe.Nothing
. If false
, it is Maybe.Just
.
import j8plus.types.Maybe;
Maybe.nothing().isNothing(); // true
Maybe.maybe(1).isNothing(); // false