Objects Utility Class - Sip of Java
Billy Korando on May 28, 2023The Objects utility class, introduced with JDK 1.7, provides a rich API for performing many common operations on objects. Over many JDK releases, the Objects class has seen several updates, with significant updates in JDK 8 and 9 and minor updates in JDK 16 and 19. Let’s explore how to use the Objects class.
Comparing Objects
Objects
provides several options for comparing the values of two objects. A key advantage of using the Objects
implementation is their null
safety.
equals()
Below is an example of comparing two records.
record RaceTime(String runnerName, Duration time) {}
RaceTime nullValue = null;
RaceTime billy =
new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime copyOfbilly =
new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime nicolai =
new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));
nullValue.equals(billy);//NPE
Objects.equals(nullValue, billy);// false
Objects.equals(billy, nicolai);// false
Objects.equals(billy, copyOfbilly);// true
deepEquals()
Objects
also has deepEquals()
for two arrays. Unlike normal equals()
, this will compare the values stored in the arrays, which should result in more consistent results. Fundamentally this method passes through to Arrays.deepEquals()
.
record RaceTime(String runnerName, Duration time) {}
RaceTime billy =
new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime nicolai =
new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));
RaceTime[] raceTimes1 = new RaceTime[] { billy, nicolai };
RaceTime[] raceTimes2 = new RaceTime[] { billy, nicolai };
Objects.equals(raceTimes1, raceTimes2);// false
Objects.deepEquals(raceTimes1, raceTimes2);// true
compare()
Objects
also has a compare()
, which can take two objects and a Comparator<T>
. compare()
is one of the few non null
safe methods in Objects
, as there isn’t a reasonable return in the event one of the arguments is null
.
record RaceTime(String runnerName, Duration time) {}
class RaceTimeComparator implements Comparator<RaceTime> {
@Override
public int compare(RaceTime o1, RaceTime o2) {
return o1.time.compareTo(o2.time);
}
}
RaceTime billy =
new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime nicolai =
new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));
Objects.compare(billy, nicolai, new RaceTimeComparator());//-1
Objects.compare(null, nicolai, new RaceTimeComparator());//NPE
String and HashCode
Objects
also provides methods for converting an object to its String
and hashcode values. Again the primary advantage of these methods is their null safety.
Converting to String
One of the more interesting methods is toString(obj, nullDefault)
, which provides a default value in the event of a null
. toIdentityString(obj)
returns the toString()
and hashCode()
of the passed in objects as though neither of those methods has been overwritten.
record RaceTime(String runnerName, Duration time) {}
RaceTime nullValue = null;
RaceTime billy =
new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime nicolai =
new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));
Objects.toString(billy);//RaceTime[runnerName=Billy Korando, time=PT1M30S]
Objects.toString(nullValue);//null
Objects.toString(nullValue, "Did not finish");//Did not finish
Objects.toIdentityString(billy);//ObjectsExamples$1RaceTime@251a69d7
Converting to HashCode
Objects
also provide methods for converting an object to its hashcode value.
record RaceTime(String runnerName, Duration time) {}
RaceTime nullValue = null;
RaceTime billy =
new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime nicolai =
new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));
Objects.hashCode(nullValue);//0
Objects.hashCode(billy);//[HashValue]
Objects.hash(billy, nicolai);//[HashValue]
Null Checking
Objects
provides many methods for checking and handling null
.
Throwing NullPointException on null
requireNonNull(obj)
will throw a NullPointException
if the passed-in value is null
.
record RaceTime(String runnerName, Duration time) {
RaceTime{
runnerName = Objects.requireNonNull(runnerName);
time = Objects.requireNonNull(time);
}
}
Throwing NullPointException on null with a custom message
requireNonNull(obj, String)
will throw a NullPointException
with a custom message if the passed-in value is null
.
record RaceTime(String runnerName, Duration time) {
RaceTime{
runnerName = Objects.requireNonNull(runnerName, "runner name required!");
time = Objects.requireNonNull(time, "race time required!");
}
}
Returning default value on null
requireNonNullElse(obj, defaultValue)
will return the passed defaultValue
if obj
is null
.
record RaceTime(String runnerName, Duration time) {
RaceTime{
runnerName = Objects.requireNonNullElse(runnerName, "John Smith");
time = Objects.requireNonNullElse(time, Duration.ZERO);
}
}
Using Suppliers
Objects
also provides the methods requireNonNull(obj, Supplier<String>)
and T requireNonNullElseGet(T, Supplier<T>)
, which can use a Supplier
for providing the message or default value. These should only be used if there might be a significant performance impact from generating the message or default value.
record RaceTime(String runnerName, Duration time) {
static Supplier<String> noNameMsgSupplier = () -> "runner name required!";
static Supplier<String> noTimeMsgSupplier = () -> "race time required!";
RaceTime{
runnerName = Objects.requireNonNull(runnerName, noNameMsgSupplier);
time = Objects.requireNonNull(time, noTimeMsgSupplier);
}
}
record RaceTime(String runnerName, Duration time) {
static Supplier<String> noNameValueSupplier = () -> "John Smith";
static Supplier<Duration> noTimeValueSupplier = () -> Duration.ZERO;
RaceTime{
runnerName = Objects.requireNonNullElseGet(runnerName, noNameValueSupplier);
time = Objects.requireNonNullElseGet(time, noTimeValueSupplier);
}
}
Predicate null checking
Objects
also provides null checking for use in predicates, though it can also be used in other scenarios.
record RaceTime(String runnerName, Duration time) {}
RaceTime nullValue = null;
Objects.nonNull(nullValue);//false
Objects.isNull(nullValue);//true
Index Checking
Objects
also provide several options for checking the index position when traversing a File
, String
, Collection
, or similar object. Several of these methods were recently added in JDK 16.
int checkIndex(int index, int length)
int checkFromToIndex(int fromIndex, int toIndex, int length)
int checkFromIndexSize(int fromIndex, int size, int length)
long checkIndex(long index, long length)
long checkFromToIndex(long fromIndex, long toIndex, long length)
long checkFromIndexSize(long fromIndex, long size, long length)
Additional Reading
Happy coding!