This is my first attempt at the Optional type, implemented in Java. Optional, or variants of it, is a ubiquitous type typically present in functional or hybrid-functional languages. It is used for cases when it is valid for a procedure (function, method, etc) to terminate with no result. In Java, the idiomatic way to do this is to return the dreaded null-pointer, or return null
. However, the possibility of a "no result"-result, or null
, cannot be expressed by the type system, and is typically the source of numerous NullPointerExceptions
where the caller of a method forgets to check if the returned value is, in fact, an instance before dereferencing it. The Optional type explicitly expresses that a value may be something, or may be nothing, at the type level.
An Optional can be constructed using the no.runeflobakk.option.Optional.optional(..)
factory method. It should be statically imported and used like this:
String string = //get string or null from somewhere
Optional<String> optString = optional(string);
Now, optString
is either an instance of Some<String>
or the singleton None
. It can be queried using optString.isSome()
to determine if the string is indeed present or not, and if it is, it can be obtained with optString.get()
.
The procedure described above looks more like a glorified old-fashioned null check, and not that much of any added value. The != null
has been replaced with a call to isSome()
, and using the value requires a tedious call to get()
. If anything, we have managed to somewhat force to check for the presence of the value, which can be argued to be a slight improvement, but it isn't in any way spectacular.
An Optional can be viewed as an Iterable
which is either empty or has exactly one element (analogous to a list with either zero or one element). In fact, Optional
implements the Iterable
interface, which makes it applicable for use with a standard for
-loop in Java. Now, it may seem strange to "iterate" one value, but what we are actually using the loop for is to say "if present, then assign and execute block".
Using optString
from the previous example, we can print the string if it exists:
for (String s : optString) { System.out.println(s); }
We can even write it on one line, and it doesn't look half-bad.
This implementation relies heavily on functional interfaces from Commons-Collection with Generics to provide support for some functional programming concepts.
To add Commons-Collection with Generics to your Maven-project, use this dependency:
<dependency>
<groupId>net.sourceforge.collections</groupId>
<artifactId>collections-generic</artifactId>
<version>4.01</version>
</dependency>
By default, the optional(..)
factory method treats null
as nothing, and any instance as something. But it may be situations where you want to adapt how values are viewed. For instance, you may want to treat both null and empty strings as nothing. This can be achieved by using Optional<V>.optional(Predicate, V)
, where the predicate decides if the value should be treated as "present".
Say we have an implementation of Predicate that matches non-blank strings:
public final Predicate<String> nonBlank = new Predicate<String>() {
public boolean evaluate(String string) {
return !StringUtils.isBlank(string); //StringUtils is from Apache Commons Lang
}
};
Then we can use the predicate with optional
like this:
for(String s : optional(nonBlank, "")) { throw new RuntimeException(); }
No exception will be thrown in the above code. If we did not include the predicate, String s
would be assigned the empty string, and a RuntimeException
would have been thrown.
Optional
also supports mapping (or transforming) its value to something else. The mapping operation is safe; you can perfectly map a non-existing value. The key is that the .map(..)
method returns a new Optional of the result type of the mapping function (or None
if the mapper function maps to null
or any other undefined value as decided by an optional predicate). The mapping functions are implementations of the Transformer<I, O> interface.
Say we have a string that may or may not be numeric and we are only interested in it if indeed it is numeric. We need a predicate to decide if a string is numeric:
public final Predicate<String> numeric = new Predicate<String>() {
public boolean evaluate(String string) {
return !StringUtils.isNumeric(string);
}
};
We also need to parse the string as an int
to be able to use it, so we define a transformer for that:
public final Transformer<String, Integer> toInt = new Transformer<String, Integer>() {
public Integer transformer(String string) {
return Integer.parseInt(string);
}
};
The two functions defined above can then be used with optional
like this:
String mayBeNumber = //get the number
for (int i : optional(numeric, mayBeNumber).map(toInt)) {
// do something with i
}
If mayBeNumber
is numeric, the string is converted to an integer and assigned to int i
, and the block is executed. If mayBeNumber is not numeric, the None
singleton instance is returned from both optional(..)
, and map(..)
, and following no assignment is done to i
.
Everything in this project is licensed under WTFPL.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <[email protected]>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
If you use this project for anything, I would appreciate some kind of attribution, but this is in no way required. The license terms still apply: you just do WTF you want to!