The Objects class in Java contains some static utility functions. These functions primarily perform null tests to avoid a subsequent NullPointerException when object methods are called.
Built-In Null Tests for equals(...)/hashCode()
For example, if the object variable name of a person is null, then name.hashCode() can’t simply be called without a NullPointerException following the call. Three methods of Objects perform null tests before being passed to the Object method: equals(…), hash- Code(), and toString().
class java.util.Objects
- static boolean equals(Object a, Object b): Returns true if both arguments are either null or if equals(b) also returns true; otherwise, this method returns false. That Objects.equals(null, null) returns true makes sense, and thus, the method saves you from some manual testing.
- static int hashCode(Object o): Returns 0 if o is null; otherwise, hashCode().
- static int hash(Object… values): Calls hashCode() on each object in the values collection and concatenates it to a new hash value. The implementation is simply a return Arrays.hashCode(values). Using the method is rather expensive due to the construction of the varargs array and possible boxing operations for primitive values.
- static <T> int compare(T a, T b, Comparator<? super T> c): Returns 0 if a and b are both either null or the Comparator declares the objects a and b to be equal. If a and b are both not null, the return is compare(a, b). If only a or b is null, the result depends on the Comparator and the order of the parameters.
Example: Let’s explore an overridden hashCode() method for a player, where the player name should go into the hash value.
result = 31 * result + ((name == null) ? 0 : name.hashCode());
With Objects.hashCode(Object), the null test can be omitted because the static method already performs it. Consider the following example:
result = 31 * result + Objects.hashCode( name );
Objects.toString(…)
Another static method is Objects.toString(Object). This method exists in the class for symmetry reasons since toString() is one of the standard methods of the Object class. The method doesn’t need to be used because a corresponding method already exists with String.valueOf(...).
class java.util.Objects
- static String toString(Object o): Returns the string "null" if the argument is null; otherwise, toString().
Note: The String.valueOf(...) method is overloaded and better suited for primitive arguments than Objects.toString(Object), which always requires wrapper objects to be created first. Although String.valueOf(3.14) and Objects.toString(3.14) look the same, in the second case, a wrapper double object comes into play.
null Checks with Built-In Exception Handling
In the preceding methods, null is treated as a special case, and exceptions are avoided. For example, Objects.toString(null) or Objects.hashCode(null) are fine and “work around” null. This flexibility is not always useful, however, because traditionally you want to avoid null as an argument and in returns. Therefore, a good practice is to first test in a method body to determine whether the arguments are non-null, unless null is absolutely desired.
For tests that check whether references are non-null, Objects provides a couple of requireNonNull*(...) methods to carry out null checks and throw a NullPointerException if an error occurs. These tests are useful for constructors and setters that should initialize values but want to prevent null from being passed through.
Example: The setName(...) method is supposed to prohibit a name argument equal to null. Consider the following example:
public void setName( String name ) {
this.name = Objects.requireNonNull( name );
}
Alternatively, the following error message is possible:
public void setName( String name ) {
this.name = Objects.requireNonNull( name, "Name must not be null" );
}
class java.util.Objects
- static <T> T requireNonNull(T obj): Throws a NullPointerException if obj is null. Otherwise, the obj is the return. The declaration is generic and to be understood in such a way that the parameter type is the same as the return type.
- static <T> T requireNonNull(T obj, String message): Like requireNonNull(obj), except that the NullPointerException message is determined.
- static <T> T requireNonNull(T obj, Supplier<String> messageSupplier): Like requireNonNull(obj, message), but the message comes from messageSupplier. This is useful for messages that are more expensive to build because the Supplier defers the cost of creating the string until a NullPointerException actually occurs because only then is the message necessary.
- static <T> T requireNonNullElse(T obj, T defaultObj): Returns the first object that’s not null. defaultObj must not be null; otherwise, a NullPointerException will follow. Implemented as return (obj != null) ? obj : requireNonNull (defaultObj, "defaultObj");.
- static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier): Returns the first object that isn’t If obj is null, the method gets the reference from the supplier, which then must not return null; otherwise, a NullPointerException will follow.
Tests for null
Behind isNull(Object o) and nonNull(Object o), a simple test is available for o == null or o != null.
class java.util.Objects
- static boolean isNull(Object obj)
- static boolean nonNull(Object obj): Returns true if obj is null or non-null; otherwise, false.
In normal program code, developers won’t use these methods, but these methods are useful for method references so that, for example, stream.filter(Objects::nonNull) can be read.
Checking Index-Related Program Arguments for Correctness
It’s important to check value ranges and throw exceptions, such as IllegalArgumentException or IndexOutOfBoundsException, in case of errors to prevent incorrect values from sneaking into the object.
Other methods from Objects check valid value ranges of index-based methods and throw an IndexOutOfBoundsException in case of an error.
class java.util.Objects
- static int checkIndex(int index, int length)
- static long checkIndex(long index, long length)
- static int checkFromToIndex(int fromIndex, int toIndex, int length)
- static long checkFromToIndex(long fromIndex, long toIndex, long length)
- static int checkFromIndexSize(int fromIndex, int size, int length)
- static long checkFromIndexSize(long fromIndex, long size, long length)
Example: The following example is an implementation of the get(int) method in java.util.ArrayList:
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
The private elementData(int index) method directly accesses the internal array with elementData[index], which could be larger, however, because the implementation of ArrayList has a certain buffer size. Therefore, prior index-verification is necessary.
Editor’s note: This post has been adapted from a section of the book Java: The Comprehensive Guide by Christian Ullenboom.
Comments