In this package you will find utility classes to simplify language related topics.
This classes are intended to simplify checks on conditional statements.
I collected into this classes all the most common checks I need to do in everydays coding.
In my experience, the most common checks are:
The methods of this two classes can be used in conditional statements like:
if( Is.empty(collection) ) // load data
But can also be used as predicates in functional programming like:
Stream<String> stream = ... stream.filter( IsNot::blank ).forEach( /* do something */ );
Some of the most annoying code lines are those related to checking the consistency of methods arguments. For example you are writing a constructor with parameters and you need to check that the provided arguments are properly valued.
Usually you will write something like this:
public SomeType( String string, Collection<?> collection, Integer integer ) { if( StringUtils.isBlank(string) ) throw new IllegalArgumentException( "The string argument cannot be blank" ); if( collection == null || collection.isEmpty() ) throw new IllegalArgumentException( "The collection argument cannot be empty" ); if( integer == null || integer <= 0 ) throw new IllegalArgumentException( "The integer argument must be positive" ); this.string = string; this.integer = integer; this.collection = collection; }
When I saw the method Objects.requireNonNull(Object,String) for the first time, I found it a great idea!
Unfortunately the class java.util.Objects contains only this single check.
Therefore I decided to create the class Require where I collected the most common requirements I need to check on methods arguments.
Using the utility Require the previous constructor becomes:
public SomeType( String string, Collection<?> collection, Integer integer ) { this.string = Require.nonBlank( string, "The string argument cannot be blank" ); this.integer = Require.trueFor( integer, () -> integer > 0, "The integer argument must be positive" ); this.collection = Require.nonEmpty( collection, "The collection argument cannot be empty" ); }
As you can see this way checking the arguments consistency becomes quicker to write, easier to understand and more elegant.
In my experience, the most common requirements for methods arguments are:
In addition there are two general purpose methods:
All the methods of the class Require throw a RequirementFailure exception.
This interface is intended to be used with the utility classes Is, IsNot and Require described before.
The classes Collection, Map and String share the same method isEmpty(). Unfortunately the java libraries do not implement a common interface to emphasize that instances of those classes may be empty.
The aim of the Emptily interface is to highlight that instances of the implementing class may have a content of may be empty.
A common use case is when you write a class to contain the response of a service. If the call fails, instead of returning null you may want to implement the EmptyObject pattern. Using Emptily you can write your data model like:
public class ServiceResponse implements Emptily
and you can check if the response is empty using
ServiceResponse response = service.call(); if( IsNot.empty(response) ) // Do something
or you may want to require the response to be not empty using
ServiceResponse response = Require.nonEmpty( service.call(), "The service response cannot be empty" );
This classes are intended to provide a quick, concise and elegant way to implement the methods Object.hashCode(), Object.equals(Object) and Object.toString().
Most of the IDEs provide tools to generate this methods in an automated way but this approach has some drawbacks. For equals and hashCode the generated code is weird and usually hard to understand. By using this utility classes it will become much more concise and of immediate understanding.
For example some code like this:
public boolean equals( Object obj ) { if( obj == this ) return true; if( getClass() != obj.getClass() ) return false; ThisClass other = (ThisClass) obj; if( field == null ) { if( other.field != null ) return false; } else if( ! field.equals(other.field) ) return false; if( array == null ) return other.array == null; else if( other.array == null ) return false; if( array.length != other.array.length ) return false; for( int i=0; i<array.length; i++ ) { Object o1 = array[i]; Object o2 = other.array[i]; boolean e = o1==null ? o2==null : o1.equals( o2 ); if( ! e ) return false; } return true; } public boolean hashCode() { int hashCode = 31 hashCode *= field1 & 0xFFFFF800 hashCode = hashCode ^ (field2 << 11); hashCode += field3 << 6; hashCode += field4 == null 0 : field4.hashCode(); return hashCode ^ (hashCode >>> 32); }
will become:
public boolean equals( Object other ) { return Equals.ifSameClass( this, other, o -> o.field, o -> o.array ); } public boolean hashCode() { return Hashcode.of( field1, field2, field3, field4 ); }
Much better!
To compute hash codes there is already Objects.hash(Object…) why should I use your Hashcode.of(Object…)?
Actually you don’t need to use the Hashcode class, unless you want to store the hash code outside the JVM expecting that a subsequent run of the JVM will generate the same code. This is not true for Objects.hash(Object…). If you generate hash codes (especially for enums) and store them you may notice that the same object can generate different hash codes in different runs of the JVM. The Hashcode utility has been developed to avoid this misbehaviour and generate always the same hashcode.
For toString, if you use the code generated by your favorite IDE, all the objects will have the same string format. If you want to customize the output you need to rewrite the toString method manually.
Using the ToString utility you have 4 builtin layouts and, in a few steps, you can implement your own preferred layout.
For example using IntelliJ you may generate something like this:
public String toString() { return "SomeType{" + "id=" + id + ", string='" + string + '\'' + ", intArray=" + Arrays.toString(intArray) + ", stringMatrix=" + Arrays.toString(stringMatrix) + '}'; }
Using the ToString utility you can get the same result by writing:
public String toString() { return ToString.of( this ) .print( "id", id ) .print( "string", string ) .print( "intArray", intArray ) .print( "stringMatrix", stringMatrix ) .likeIntellij(); }
But, if you want to change the output in a function-like format, you just need to change the last method from likeIntellij() to likeFunction(). Finally you can implement you own ToString.Printer and get your favorite layout.
Everyone that worked with comparable objects can tell that dealing with the java.lang.Comparable interface is such a pain!
Java libraries have tried to work around the problem by introducing the java.util.Comparator interface, but sometimes you still need to use plain old comparable objects (e.g. if you work with BigDecimals).
In those cases you may want to perform some comparisons like:
( a < b and b != c ) or ( b == c and c >= d )
and to do so you need to write some almost unreadable code
( a.compareTo(b) < 0 && b.compareTo(c) != 0 ) || ( b.compareTo(c) == 0 && c.compareTo(d) >= 0)
It would be nice if the java.lang.Comparable interface would be improved with some convenience (default) method like
default boolean lt( T o ) { return compareTo( o ) < 0; }
This way the previous check will become something like
( a.lt(b) && b.ne(c) ) || ( b.eq(c) && c.ge(d) )
The last statement is much similar to the mathematical notation and therefor it is of immediate understanding.
I suggested this improvement to the java community and I hope that it will be implemented, but in the meantime I have created the Comparative interface for this purpose.
By implementing the Comparative interface you will have objects that implement Comparable and can be compared using the convenience methods described above.