It
was announced
approximately 18 months ago that JDK 7 would include a
new java.util.Objects
classthat would "hold commonly-written utility methods."
As part of this
announcement, Joe Darcy asked
the community, "What other utility methods would have broad enough use and
applicability to go into a common java.util class?" There were forums and
posts on the matter and I blogged
about this forthcoming class. The JDK 7 preview release includes
this class and it can be tried out now. In this post, I look at use of most of
the methods provided by this class and look at how NetBeans 6.9 already
uses this class in some of its auto-generated methods.
The java.util.Objects class is new to JDK 7 and its Javadoc states that the class is "since 1.7" and describes the class as: "This class consists of static utility methods for operating on objects. These utilities include null-safe or null-tolerant methods for computing the hash code of an object, returning a string for an object, and comparing two objects." There are currently nine static methods with public accessibility: a compare method, a deepEquals method (for comparing arrays for equality), an equalsmethod, a hash code calculation method potentially for multiple objects, a hashCode method for a single object, overloaded requireNonNull methods, and overloaded toString methods.
There are some general themes associated with the methods provided by the new
The second major theme relevant to the methods of the new
The java.util.Objects class is new to JDK 7 and its Javadoc states that the class is "since 1.7" and describes the class as: "This class consists of static utility methods for operating on objects. These utilities include null-safe or null-tolerant methods for computing the hash code of an object, returning a string for an object, and comparing two objects." There are currently nine static methods with public accessibility: a compare method, a deepEquals method (for comparing arrays for equality), an equalsmethod, a hash code calculation method potentially for multiple objects, a hashCode method for a single object, overloaded requireNonNull methods, and overloaded toString methods.
There are some general themes associated with the methods provided by the new
Objects
class. The methods tend to
increase null safety. In other words, use of these methods is something I'd be
happy to add to my list of Java
NullPointerException handling techniques. When one desires an
instance's hash code or string representation or one wants to compare an
instance to another instance, it is typically prudent to first check the object
reference for null to avoid a NullPointerException occurring
before the hash code, String representation, or comparison can be made. By
moving these common operations out of one of the objects being acted upon into
this separate Objects
class,
the external class can check for null rather than forcing the object itself to
be checked explicitly every time.The second major theme relevant to the methods of the new
Objects
class is that these methods
encapsulate functionality that many developers have written on their own or
that other non-standard libraries supply. This new Objects
class provides a relatively
concise, standardized approach to invoking this common functionality. As will
be seen in this post, it makes it easy to clean up commonly implemented methods
such as overridden toString, equals,
and hashCode methods
and to enforce non-null parameter contract constraints.Unlike the ever-present java.lang.Object class, but like fellow classes in the java.util package, the
java.util.Objects
class
must be explicitly imported in Java classes that make use of it. TheObjects
class does NOT need to be
explicitly imported when used in Groovy because Groovy
automatically imports classes in the java.util
package.
Because all examples in this post are written in Java, the java.util.Objects
class
will be explicitly imported in each class.The next code listing is for a simple
Person
class. I generated this
class's hashCode()
andequals(Object)
methods
using NetBeans 6.9's "Insert Code" mechanism and was pleased to see
that (assuming I had the Java 1.7 Platform set for my project) the
relevant Objects
class
methods were used in these automatically generated methods. The automatically
generated toString()
did
not make use of either of the overloaded Objects.toString()
methods.
I assumed that might have been because the Person
class has only String
attributes and these don't need to be checked for null before the
implicit toString()
is
invoked on them. However, even when I added a more complex data type that would
lead to an NPE when its implicit toString()
was
called, NetBeans did not use theObjects.toString()
method to avoid the potential
NPE.Person.java
- package dustin.examples;
- import java.util.Objects;
- /**
- * Simple class to be used in demonstration of JDK 7's java.util.Objects class.
- *
- * @author Dustin
- */
- public class Person
- {
- private String lastName;
- private String firstName;
- /**
- * Parameterized constructor for instantiating a Person. Both a non-null first
- * name and a non-null last name must be provided (no "Madonna" or "Lindsay"
- * or "Seal" allowed here [unless you pass one name as an empty String]).
- *
- * @param newLastName This Person instance's last name; must not be null.
- * @param newFirstName This Person instance's first name; must not be null.
- * @throws NullPointerException Thrown if either provided name parameter is
- * null.
- */
- public Person(final String newLastName, final String newFirstName)
- {
- this.lastName = Objects.requireNonNull(newLastName, "Last name cannot be null.");
- this.firstName = Objects.requireNonNull(newFirstName, "First name cannot be null.");
- }
- public String getLastName()
- {
- return this.lastName;
- }
- public String getFirstName()
- {
- return this.firstName;
- }
- /**
- * NetBeans 6.9-generated equals(Object) method. It used
- * Objects.equals(Object, Object) to avoid the need to check for null on any
- * references before comparing them. This can really clean up equals method
- * implementations.
- *
- * @param obj Object to be compared to me for equality.
- * @return {@code true} if the provided object is considered equal to me;
- * {@code false} otherwise.
- */
- public boolean equals(Object obj)
- {
- if (obj == null)
- {
- return false;
- }
- if (getClass() != obj.getClass())
- {
- return false;
- }
- final Person other = (Person) obj;
- if (!Objects.equals(this.lastName, other.lastName))
- {
- return false;
- }
- if (!Objects.equals(this.firstName, other.firstName))
- {
- return false;
- }
- return true;
- }
- /**
- * NetBeans 6.9-generated hashCode(). It used Objects.hashCode(Object)!
- *
- * @return Hash code for this instance.
- */
- public int hashCode()
- {
- int hash = 5;
- hash = 97 * hash + Objects.hashCode(this.lastName);
- hash = 97 * hash + Objects.hashCode(this.firstName);
- return hash;
- }
- @Override
- public String toString()
- {
- return this.firstName + " " + this.lastName;
- }
- }
The NetBeans-generated
hashCode()
method makes use of Objects.hashCode(Object) to get the individual hash codes of each of its constituent attributes. The advantage of doing this is that each attribute does not need to be checked for null before asking for its hash code. The null checking is still implicitly performed (and zero is returned if the object is null), but the code is much cleaner.The NetBeans-generated
equals(Object) method
makes use of Objects.equals(Object, Object) to safely compare the current object's attributes to the provided object's attributes. This method is null-safe and returns true if both attributes being compared are null and returns false if either is null without the other being null. If both compared attributes are non-null, then a standard equality check is made. The Objects.equals(Object,Object)
method provides consistent null-safe equality checking with much cleaner code than could be done before this.The
Person
class listed above is used by the next code listing, which is the main code listing demonstrating the majority of the methods on the new Objects
class.ObjectsClassDemo.java
- package dustin.examples;
- import java.util.Objects; // must be explicitly imported
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /**
- * Simple demonstration of the new java.util.Objects class coming with JDK 7.
- */
- public class ObjectsClassDemo
- {
- private static final Logger LOGGER = Logger.getLogger(ObjectsClassDemo.class.getName());
- /**
- * Demonstrate usage of Objects.requireNonNull(Object).
- *
- * @param shouldNotBeNull String object be passed to Objects.requireNonNull(Object).
- */
- private static void demoObjectsClassNullness(final String shouldNotBeNull)
- {
- String stringToUse = null;
- try
- {
- stringToUse= Objects.requireNonNull(shouldNotBeNull);
- }
- catch (NullPointerException npe)
- {
- LOGGER.severe(npe.toString());
- }
- LOGGER.log(Level.INFO, "Provided String was: ''{0}''", stringToUse);
- }
- /**
- * Demonstrate usage of Objects.requireNonNull(Object,String). This overloaded
- * version of Objects.requireNonNull is generally preferable because the
- * second (String) parameter is the "message" portion of the NullPointerException
- * that is generated. Without this parameter, the message portion is empty.
- *
- * @param shouldNotBeNull String object to be passed to
- * Objects.requireNonNull(Object,String) where the first (Object) parameter
- * is the object that should not be null and the second (String) parameter
- * is the message to display if the first parameter is null.
- */
- private static void demoObjectsClassNullness(
- final String shouldNotBeNull,
- final String messageIfNull)
- {
- String stringToUse = null;
- try
- {
- stringToUse = Objects.requireNonNull(shouldNotBeNull, messageIfNull);
- }
- catch (NullPointerException npe)
- {
- LOGGER.severe(npe.toString());
- }
- LOGGER.log(Level.INFO, "Provided String was: ''{0}''", stringToUse);
- }
- /**
- * Demonstrate use of Objects.toString(Object) with default message if provided
- * object is null.
- *
- * @param objectToStringify Object to call Objects.toString(Object) on.
- */
- private static void demoNullSafeToStringDefault(
- final Object objectToStringify)
- {
- LOGGER.log(Level.INFO, "toString(): {0}", Objects.toString(objectToStringify));
- }
- /**
- * Demonstrate use of Objects.toString(Object, String) with customized String
- * used to "toString()" when the provided object is null.
- *
- * @param objectToStringify Object to call Objects.toString(Object) on.
- * @param toStringIfObjectIsNull String to be shown as result of "toString()"
- * on a null reference.
- */
- private static void demoNullSafeToStringCustomized(
- final Object objectToStringify, final String toStringIfObjectIsNull)
- {
- LOGGER.log(Level.INFO, "toString(): {0}", Objects.toString(objectToStringify, toStringIfObjectIsNull));
- }
- /**
- * Demonstrate Objects.hash(). The Objects.hashCode() method is also
- * demonstrated and it is handy to be able to safely get a hash code without
- * explicit null check (0 is returned by Objects.hashCode(Object) if the
- * provided Object reference is null). It is also important to note that
- * calling Objects.hash(Object...) on a single object will NOT result in the
- * same hash code returned from Objects.hashCode(Object) on that same object.
- *
- * @param objectsToHash One or more objects to hash.
- */
- private static void demoHash(final Object firstObjectToHash, final Object ... objectsToHash)
- {
- final int numberObjects =
- objectsToHash.length
- + (firstObjectToHash != null ? 1 : 0);
- final int multipleObjectsHash = Objects.hash(objectsToHash);
- LOGGER.log(Level.INFO, "Hash Code for {0} objects: {1}",
- new Object[]{numberObjects, multipleObjectsHash});
- LOGGER.log(Level.INFO, "Hash code for first object ({0}) of {1} object(s) is: {2}",
- new Object[]{Objects.toString(firstObjectToHash), numberObjects, Objects.hashCode(firstObjectToHash)});
- }
- /**
- * Demonstrate Objects.equals(Object, Object) method.
- *
- * @param firstObject First object to be compared by Objects.equals(Object,Object).
- * @param secondObject Second object to be compared by Objects.equals(Object,Object).
- */
- private static void demoEquals(final Object firstObject, final Object secondObject)
- {
- final String aproposPhrase = Objects.equals(firstObject, secondObject)
- ? " is equal to "
- : " is NOT equal to ";
- LOGGER.log(Level.INFO, "{0}{1}{2}",
- new Object[]{Objects.toString(firstObject), aproposPhrase, Objects.toString(secondObject)});
- }
- /**
- * Main demonstration executable.
- *
- * @param arguments Command-line arguments; none anticipated.
- */
- public static void main(final String[] arguments)
- {
- demoObjectsClassNullness("Dustin");
- demoObjectsClassNullness(null);
- demoObjectsClassNullness("Dustin", "The String you passed is null!");
- demoObjectsClassNullness(null, "The String you passed is null!");
- final Person person = new Person("Smith", "William");
- Person nullPerson = null;
- try
- {
- nullPerson = new Person("Dump", null);
- }
- catch (NullPointerException npe)
- {
- LOGGER.severe(npe.toString());
- }
- demoNullSafeToStringDefault(person);
- demoNullSafeToStringDefault(nullPerson);
- demoNullSafeToStringCustomized(person, "No such person");
- demoNullSafeToStringCustomized(nullPerson, "No such person");
- demoHash(person, "Dustin");
- demoHash("Dustin", person);
- demoHash(person);
- demoHash("Dustin");
- demoHash(nullPerson);
- final Person person2 = new Person("Smith", "Barney");
- final Person person3 = new Person("Smith", "Barney");
- demoEquals(person, person2);
- demoEquals(person, nullPerson);
- demoEquals(person2, person3);
- demoEquals(nullPerson, null);
- }
- }
The
ObjectsClassDemo
class contained in the code listing above demonstrates most of the methods on the Objects
class. The output from running the above class's main
function is shown next.Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoObjectsClassNullness INFO: Provided String was: 'Dustin' Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoObjectsClassNullness SEVERE: java.lang.NullPointerException Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoObjectsClassNullness INFO: Provided String was: 'null' Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoObjectsClassNullness INFO: Provided String was: 'Dustin' Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoObjectsClassNullness SEVERE: java.lang.NullPointerException: The String you passed is null! Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoObjectsClassNullness INFO: Provided String was: 'null' Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo main SEVERE: java.lang.NullPointerException: First name cannot be null. Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoNullSafeToStringDefault INFO: toString(): William Smith Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoNullSafeToStringDefault INFO: toString(): null Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoNullSafeToStringCustomized INFO: toString(): William Smith Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoNullSafeToStringCustomized INFO: toString(): No such person Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash Code for 2 objects: 2,058,375,062 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash code for first object (William Smith) of 2 object(s) is: -2,111,928,853 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash Code for 2 objects: -2,111,928,822 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash code for first object (Dustin) of 2 object(s) is: 2,058,375,031 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash Code for 1 objects: 1 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash code for first object (William Smith) of 1 object(s) is: -2,111,928,853 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash Code for 1 objects: 1 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash code for first object (Dustin) of 1 object(s) is: 2,058,375,031 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash Code for 0 objects: 1 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoHash INFO: Hash code for first object (null) of 0 object(s) is: 0 Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoEquals INFO: William Smith is NOT equal to Barney Smith Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoEquals INFO: William Smith is NOT equal to null Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoEquals INFO: Barney Smith is equal to Barney Smith Mar 26, 2011 11:35:26 PM dustin.examples.ObjectsClassDemo demoEquals INFO: null is equal to null
The above code and its corresponding output lead to several observations:
- The overloaded Objects.requireNonNull methods are useful for easily checking parameters to ensure that they are not null and throwing a
NullPointerException
if any is null.- The Objects.requireNonNull(T) method that only accepts a single parameter throws a
NullPointerException
without a message portion while theObjects.requireNonNull(T, String) method provides the String (second) parameter as the message portion of theNullPointerException
. I prefer the latter because one of my personal pet peeves is exception's without a message String.
- The Objects.requireNonNull(T) method that only accepts a single parameter throws a
- The overloaded Objects.toString(Object) methods are similar to the
requireNonNull
methods in that uses a "default" while the other allows a customized String to be provided. Both of these methods ensure that a String representation of some sort is provided, even for null references.- The method accepting a single object returns the String "null" if the object passed to Objects.toString(Object) is null while the method accepting a second (String) parameter returns that provided String when the Object provided toObjects.toString(Object, String) is null.
- The Objects.hash(Object...) method and the Objects.hashCode(Object) methods provide null-safe functionality for accessing hash codes.
- The Objects.hash(Object...) method provides a hash code constructed from multiple objects. Note that this method does NOT return the same hash code for a single provided object as would be returned for the same single object is passed to Objects.hashCode(Object).
- The Objects.hashCode(Object) method is for acquiring a hash code for a single object in a null-safe way. A zero is returned by this method if the provided object is null.
- Objects.equals(Object,Object) provides a null-safe way to check two objects for equality. As described above, it returns
true
if both arguments are null.
I don't specifically focus on the Objects.deepEquals(Object,Object) method or theObjects.compare(T,T,Comparator) method here. They perform as you'd expect from their names. The
deepEquals
method is used to compare objects that are arrays using Arrays.deepEquals. Thecompare
method returns zero if both provided objects comparison would normally equal zero or if both provided references are null.Other References
Additional coverage of the
java.util.Objects
class can be found in Java 7: The New java.util.Objects Class, The Java 7 Features Bound to Make Developers More Productive, and JDK 7: What Frequently Rewritten Methods Should be Included in java.util.Objects?Conclusion
The java.util.Objects class will provide Java developers with a concise, standardized approach for performing tasks that they've previously had to use non-standard and/or verbose custom code to resolve. Even when developers use IDEs to generate their code, the benefit of more concise and readable code remains. As shown in this blog post, NetBeans 6.9 is already capable of generating code that makes use of the
Objects
class.
No comments:
Post a Comment