NullableJ
contains methods for dealing with null
.
It is designed to used with Lombok @ExtensionMethod (see here and here).
This means it will be the most beneficial when used with @ExtensionMethod
although you can use the utilities without that.
The use of Lomlok's extension methods particularly magnify the value of null-safty done to all the utility methods.
For example, you can write code like this...
System.out.println("String: " + str._or(" <not-given>"));
// ^^^ and Elvis operator, anyone?
A slightly bigger example, consider the following code.
public class SaleReport {
public BigDecimal totalYearlySaleByPart(String partNumber, Color color, int year) {
val item = itemService.findItem(partNumber, color);
val salesByYear = saleStatService.findItemSalesByYear(item);
return salesByYear.get(year).stream().map(Sale::getTotal).collect(reducing(ZERO, BigDecimal::add));
}
}
This method is very readable but a lots of things can go wrong.
- there might be no item for the
partNumber
andcolor
. - the legacy
saleStatService.findItemSalesByYear(item)
may returnnull
if item isnull
. - there might be no item in
salesByYear
map.
With NullableJ
methods, we may adjust the method like this.
@ExtensionMethod({ NullableJ.class })
public class SaleReport {
public BigDecimal totalYearlySaleByPart(String partNumber, Color color, int year) {
val item = itemService.findItem(partNumber, color);
val salesByYear = item._isNotNull() ? saleStatService.findItemSalesByYear(item) : null;
return salesByYear._get(year)._stream$().map(Sale::getTotal).collect(reducing(ZERO, BigDecimal::add));
}
}
Spot the differences?
That exactly the point.
NullableJ
methods are designed to be used with Lombok's @ExtensionMethod
to provide natural way to handle null
.
NullableJ
has may often-used methods that are ready.
If you need more, you can create yourown (in yourown class, of course).
Most of the methods in NullableJ is very straightforward.
Methods that returns primitive data are the easiest to reason with.
For example: _equals
returns true
if the given object equals to another given object (it basically uses Objects.equals(...)
method).
Non-primitive type methods that will not returns null are _orXXX()
method (unless you call _or(null)
of course - but don't do it).
The methods that return non-primitive might return null
.
This might be surprise to many as the library to help dealing with null
should not be returning null
for any reason, right?
But there is a reason and that is to allow you to chain the call.
For example: _toString
will returns a string representation of the given object or null
.
Then, the method like _or()
can be used to further specify what to do next.
Like, in case of array or collection,
you may want to use empty square brackets.
System.out.println(obj.getStrings()._toString()._or("[]"));
Methods that deal with collections such as _get()
or _first()
are also return null
.
If the methods, however, returns array, collection, map or stream such as _toList()
, stream$()
, they will returns empty value.
The last group of methods are those _whenXXX
and _as
methods.
These methods allow us to test the given value if it fit a certain criteria.
These methods return Otherwise
object.
The otherwise value acts very similar to Nullable
(or Optional
).
The value is present or not depending on whether or not the criteria was met.
The different between Nullable
and Otherwise
is that the later still have access to the original value.
See Otherwise
page for more info.
Here are some more examples of methods:
Methods on Object O
O._isNull()
/O._isNotNull()
: checks if the object isnull
or not.O._equalsTo(O)
/O._notEqualsTo(O)
: checks if the object equals to another object or not (useObjects::equals
).O._toString()
: calls thetoString()
method of the object if notnull
and returnnull
otherwise.O._or(O)
: returns the object if notnull
otherwise return another object.O._orGet(Supplier<O>)
: returns the object if notnull
otherwise return the value from the supplier.O._orNullValue(Class<O>)
: returns the object if notnull
otherwise call NullValues to get the null value of the given class.O._orNullValue(Class<O>)
: returns the object if notnull
otherwise call NullValues to get the null value of the given class.O._toNullable()
: wraps the object withNullable
.O._toOptional()
: wraps the object withOptional
.O._whenNotNull()
: wraps the object withOtherwise
.O._when(Predicate<O>)
: returns theOtherwise
of the object if the predicate returnstrue
otherwise returnsOtherwise
ofnull
with the original value.O._as(Class<O>)
: returns theOtherwise
of the object if it is an instance of the given type otherwise returnsOtherwise
ofnull
with the object.O._map(Function<O, T>)
,_mapTo(Function<O, T>)
,_mapBy(Function<O, T>)
,_mapFrom(Function<O, T>)
: if notnull
, uses the given function to transform the object. Otherwise, returnnull
.
Methods on number object N
N._or(P)
: returns the number object as primitive number type if notnull
otherwise returns the given primitive number.
Methods on String S
S._length()
: returns length of the string or 0 if the string isnull
.S._isEmpty()
: returns whether the string isnull
or have 0 length.S._isBlank()
: returns whether the trimmed value if the string isnull
or have 0 length.S._trimToNull()
: returns the trimmed value of the string if the trimmed value is notnull
or empty. If the string isnull
or the trimmed value is empty, returnnull
.S._trimToEmpty()
: returns the trimmed value of the string if the trimmed value is notnull
. If the string isnull
, return an empty string.S._contains(S)
: returns if the string contains the given string. Return false if the string isnull
.S._matches(S)
: returns if the string matches the given regular expression string. Return false if the string isnull
.S._matches(S)
/S._notMatches(S)
: returns if the string matches the given regular expression string. Return false if the string isnull
.S._whenContains(C)
/S._whenNotContains(C)
,S._whenMatches(S)
/S._whenNotMatches(S)
,S._whenMatches(P)
/S._whenNotMatches(P)
: returns theOtherwise
of the string if the condition met, returnsOtherwise
ofnull
with the string if the condition does not met.C._whenNotEmpty()
: returns theOtherwise
of theCharSequence
if it is not empty, returnsOtherwise
ofnull
with theCharSequence
if it wasnull
.
Methods on Array A
, Collection C
, Stream S
and Map M
A._stream$()
,C._stream$()
: returns the stream of the array/collection otherwise return empty stream if the array/collection isnull
.A._length()
: returns the length of the array otherwise return 0 if the array isnull
.A._size()
,C._size()
: returns the length of the array/collection otherwise return 0 if the array/collection isnull
.A._isEmpty()
,C._isEmpty()
,M._isEmpty()
: returns if the array/collection is empty otherwise returns true if the array/collection isnull
.A._contains(O)
,C._contains(O)
: returns if the array/collection contains the given object otherwise returns false if the array/collection isnull
.M._containsKey(O)
: returns if the map contains the given object as a key otherwise returns false if the map isnull
.M._containsValue(O)
: returns if the map contains the given object as a value otherwise returns false if the map isnull
.A._whenNotEmpty()
,C._whenNotEmpty()
,M._whenNotEmpty()
: returns theOtherwise
of the Array/Collection/Map if it is not empty, returnsOtherwise
ofnull
with the Array/Collection/Map if it wasnull
.A._toList$()
,C._toList$()
,S._toList$()
: returns the list of the array/collection/Stream otherwise returns empty list if the array/collection/Stream isnull
.A._get(index)
,List._get(index)
,M._get(key)
: returns if the value inside the array/collection/map otherwise returnsnull
if the array/collection/map isnull
.A._get(index, orFunction)
,List._get(index, orFunction)
,M._get(key, orFunction)
: returns if the value inside the array/collection/map otherwise returns value from the givenorFunction
with the index or key as the input if the array/collection/map isnull
.A._first()
,List._first()
: returns if the first value inside the array/collection otherwise returnsnull
if the array/collection/map isnull
.A._last()
,List._last()
: returns if the last value inside the array/collection otherwise returnsnull
if the array/collection/map isnull
.A._hasAll(Predicate<O>)
,C._hasAll(Predicate<O>)
: returns if all of the elements in the array/collection pass the predicate test.A._hasSome(Predicate<O>)
,C._hasSome(Predicate<O>)
: returns if the array/collection has at least one element that pass the predicate test.A._butOnly$(Predicate<O>)
,C._butOnly$(Predicate<O>)
: returns the stream after the filter with the given predicate. If the array/collection isnull
, the empty stream is returned.A._butOnly(Predicate<O>)
,C._butOnly(Predicate<O>)
: returns the list after the filter with the given predicate. If the array/collection isnull
, the empty list is returned.A._butOnlyNonNull$$(Predicate<O>)
,C._butOnlyNonNull$$(Predicate<O>)
,S._butOnlyNonNull$$(Predicate<O>)
: returns the stream after the filter our any null element. If the array/collection/stream isnull
, the empty stream is returned.S._flatMap$(Function<O Collection<T>>)
: perform the flatMap using the given mapper and automatically convert to stream. Other word, do a flatMap of the result collection. Otherwise, return empty stream if null.
Methods on Supplier S
and Function F
S._get()
: returns the value from the supplier otherwise returnsnull
if the supplier isnull
.F._get(key)
andF._apply(key)
: returns the value from the function using the key otherwise returnsnull
if the function isnull
.
Althought list above, the full list of methods may change the synchronization between this list that actual one might not be perfect.
So instead, it is recommended to find out about all the methods from looking at the test code and the source code.
Since NullableJ
methods are very small, mostly a few lines, and very easy to understand.
They also comes with JavaDoc and well covered unit tests.
It won't be painful to just looking at its test and source code.