javascalautils.Option Maven / Gradle / Ivy
/**
* Copyright 2015 Peter Nerg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javascalautils;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* Represents optional values.
* Instances of Option are either an instance of {@link Some} or {@link None}.
* {@link Some} holds a non-null value whilst {@link None} holds no value.
* The primary use case is to replace ugly null-checks.
* Consider the method:
*
*
*
*
* SomeData getDataIfExists(SomeInput input) {
* if(haveData(input) {
* return getSomeDataFromInternalStorage();
* }
* return null;
* }
*
*
*
*
* The above method causes the user to do null-checks in case the method returned a proper value or not.
* A neater way would be:
*
*
*
*
* Option<SomeData> getDataIfExists(SomeInput input) {
* if(haveData(input) {
* return new Some<>(getSomeDataFromInternalStorage());
* }
* return Option.None();
* }
*
*
*
*
* Another example is the usage of {@link java.util.Map Maps}.
* Performing a get
on a Map will either yield the value of the key or null if no such key existed.
* Consider the Map:
* Map<String, SomeData> map = ....
* To avoid null checks one could do like so:
* Option<SomeData> option = Option.apply(map.get("someKey"));
Now you have a guaranteed non-null value with which you can work
* with without performing constant null-checks.
* Statically importing methods from the companion class ({@link OptionCompanion}) to Option one can get that Scala feel of declaration.
*
*
*
* import static javascalautils.OptionCompanion.Option;
*
* Option<String> option = Option(map.get("someKey"));
*
*
*
*
* @author Peter Nerg
* @since 1.0
* @param
* The type of the value represented by this instance
*/
public interface Option extends Iterable {
/**
* This is a singleton {@link None} since it anyways cannot represent a state.
* Can also be accessed using {@link #empty()}
*/
@SuppressWarnings("rawtypes")
public static final None DEFAULT_NONE = new None();
/**
* Creates an instance of Option.
* If a null
value is provided then {@link None} is returned, else {@link Some} containing the provided value.
*
* @param
* The type for the value this Option represents
* @param value
* The value this Option shall represent
* @return The Option representing the provided value
* @since 1.0
*/
static Option apply(T value) {
return value != null ? new Some(value) : None();
}
/**
* Returns an empty Option.
* In practice this returns a {@link #DEFAULT_NONE singleton} as it anyways cannot represent a value/state.
*
* @param
* The type for the value this Option represents
* @return The default {@link None} instance
* @since 1.0
*/
static Option empty() {
return None();
}
/**
* Returns an empty Option.
* This is the same as {@link #empty()} but with the difference it provides a more Scala like feeling if the method is statically imported.
* One can use None()
as if it was a apply method on a companion object in Scala.
* E.g.
*
*
*
*
* import static javascalautils.Option.None;
*
* Option<String> opt = None();
*
*
*
*
*
* @param
* The type for the value this Option represents
* @return The default {@link None} instance
* @since 1.2
*/
@SuppressWarnings("unchecked")
static Option None() {
return DEFAULT_NONE;
}
/**
* Returns true
if this is a {@link Some} containing the provided object, else false
.
*
* @param other
* The other object to compare to
* @return If this {@link Some} contains the provided object
* @since 1.0
*/
default boolean contains(final T other) {
return exists(value -> value.equals(other));
}
/**
* Returns the count which means 1
for nonempty Option's and 0
for empty.
*
* @return The count
* @since 1.0
*/
default int count() {
// map will either return Some(1) or None upon which the else(0) will be returned.
return map(value -> 1).getOrElse(() -> 0);
}
/**
* Returns true
if this option is nonempty and the predicate p returns true
when applied to this Option's value.
*
* @param predicate
* The predicate
* @return If the predicate matches
* @since 1.0
*/
boolean exists(Predicate predicate);
/**
* Returns this Option if it is nonempty and applying the predicate p to this Option's value returns true
.
*
* @param predicate
* The predicate
* @return The Option representing the match
* @since 1.0
*/
default Option filter(Predicate predicate) {
return exists(predicate) ? this : None();
}
/**
* Returns this Option if it is nonempty and applying the predicate p to this Option's value returns false
.
*
* @param predicate
* The predicate
* @return The Option representing the match
* @since 1.0
*/
default Option filterNot(final Predicate predicate) {
// filter not is in practice just negating the result of the provided predicate
return filter(value -> !predicate.test(value));
}
/**
* Returns true
if the Option is nonempty and the predicate holds true
, else false
.
* As an {@link Option} is a zero-or-one sized collection this is in an essence exactly the same as {@link #exists(Predicate)}.
*
* @param predicate
* The predicate
* @return If the predicate matches
* @since 1.0
*/
default boolean forall(Predicate predicate) {
return exists(predicate);
}
/**
* Returns this Option's value if such exists, else {@link NoSuchElementException} is raised.
*
* @return The value of the Option
* @since 1.0
*/
T get();
/**
* Returns this Option's value if such exists, else the value provided by the supplier.
*
* @param supplier
* The supplier to use in case this is a {@link None}
* @return The value of the Option or the value provided by the supplier
* @since 1.0
*/
T getOrElse(Supplier supplier);
/**
* Returns true
if the option is an instance of {@link Some}, false
otherwise.
*
* @return true
this Option is a {@link Some}, else false
* @since 1.0
*/
default boolean isDefined() {
return exists(t -> true);
}
/**
* Returns true
if the option is an instance of {@link None}, false
otherwise
*
* @return true
this Option is a {@link None}, else false
* @since 1.0
*/
default boolean isEmpty() {
return !isDefined();
}
/**
* Returns the Option's value in an {@link Iterator} if it is nonempty, or an empty {@link Iterator} if it is empty.
*
* @return The iterator for the Option
* @since 1.0
*/
@Override
Iterator iterator();
/**
* Returns an Option consisting of the result of applying the given function to the current {@link Some}.
* Applying map to {@link None} will always yield {@link None}.
*
* @param
* The type for the return value from the function
* @param function
* The function to use
* @return The Option containing the mapped value
* @since 1.0
*/
Option map(Function function);
/**
* Returns an Option consisting of the result of applying the given function to the current {@link Some}.
* Applying map to {@link None} will always yield {@link None}.
*
* @param
* The type for the return value from the function
* @param function
* The function to use
* @return The Option containing the mapped value
* @since 1.2
*/
Option flatMap(Function> function);
/**
* Returns this Option if it is nonempty, otherwise return the result of provided by the supplier.
*
* @param supplier
* The supplier to use in case of {@link None}
* @return This Option or the one provided by the supplier
* @since 1.0
*/
Option orElse(Supplier