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
Optionalis 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
Optionalhas 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