Complete Guide to java.util.Objects class including Java 9 enhancements

1. Introduction

java.util.Objects class is part of java.util package. Object class is a final class with a private constructor. 

Objects class comprises several static utility methods. These methods can be used for parameter null checks, toString conversion, hashCode, equals and range based checks.

As of Java 8 and Java 9 update, 8 new methods were added to this class. There are 17 methods in Objects class. We will look at all the methods of this class with several examples. 

2. Content

We will discuss below methods with several examples.

public static boolean equals(Object a, Object b)

public static boolean deepEquals(Object a, Object b)

public static int hashCode(Object o)

public static int hash(Object... values)

public static String toString(Object o)

public static String toString(Object o, String nullDefault)

public static <T> int compare(T a, T b, Comparator<? super T> c)

public static <T> T requireNonNull(T obj)

public static <T> T requireNonNull(T obj, String message)

public static boolean isNull(Object obj)

public static boolean nonNull(Object obj)

public static <T> T requireNonNullElse(T obj, T defaultObj)

public static <T> 
    T  requireNonNullElseGet(T obj, Supplier<? extends T> supplier)

public static <T> 
    T requireNonNull(T obj, Supplier<String> messageSupplier)

public static
    int checkIndex(int index, int length)

public static
    int checkFromToIndex(int fromIndex, int toIndex, int length)

public static
    int checkFromIndexSize(int fromIndex, int size, int length)

3. public static
boolean equals(Object a, Object b)

equals(Object a, Object b) method is used to check for equality of two objects using equals method. 

Below is how equals method works:

  1. If both the parameters are null, then this method returns true.
  2. If either parameter is null, then this method returns false.
  3. If both parameters are not null, then the equals method returns the result of a.equals(b).

Let us look at examples.

@Test
public void testEqualsTrue() {
	boolean isEqual1 = Objects.equals("Jon", "Jon");
	Assert.assertTrue(isEqual1);

	boolean isEqual2 = Objects.equals("Jon", "Arya");
	Assert.assertFalse(isEqual2);
}

@Test
public void testEqualsNull() {
	boolean isEqual = Objects.equals("", null);
	Assert.assertFalse(isEqual);
}

@Test
public void testEqualsBothNull() {
	boolean isEqual = Objects.equals(null, null);
	Assert.assertTrue(isEqual);
}

4. public static
boolean deepEquals(Object a, Object b)

deepEquals(Object a, Object b) is used to determine equality for arrays. You can also pass anything other than arrays too, but for that deepEquals will return the result of a.equals(b).

Below is how equals method works:

  1. If both the parameters are null, then deepEquals method returns true.
  2. If either parameter is null, then deepEquals method returns false.
  3. If both parameters are not null, and both parameters are not array, then the deepEquals method returns the result of a.equals(b).
  4. If both parameters are not null, both parameters are array, and both arrays are of different types, then the deepEquals method returns the result of a.equals(b).
  5. If both parameters are not null, and both parameters are an array of same type i.e. Object[], byte[], short[], int[], long[], char[], float[], double[] or boolean[].
    1. Compare the length of the array, if equal, compare the elements.
    2. Returns true If all elements are equal and in the same sequence, else returns false. 

Let us take examples and understand this more.

Example 1: Both the parameters are null, hence deepEquals returns true.

@Test
public void deepEquals() {
	boolean isEqual = Objects.deepEquals(null, null);
	Assert.assertTrue(isEqual);
}

Example 2: Both the parameters are int[] and have the same values, hence deepEquals returns true.

@Test
public void deepEquals() {
	int[] a = new int[] { 1, 2, 3 };
	int[] b = new int[] { 1, 2, 3 };

	boolean isEqual = Objects.deepEquals(a, b);
	Assert.assertTrue(isEqual);
}

Example 3: Both parameters are int[] but their length is different, hence deepEquals returns false.

@Test
public void deepEquals() {
	int[] a = new int[] { 1, 2, 3 };
	int[] b = new int[] { 1, 2, 3, 5 };

	boolean isEqual = Objects.deepEquals(a, b);
	Assert.assertFalse(isEqual);
}

Example 4: Both parameters are int[] but their values are different, hence deepEquals returns false.

@Test
public void deepEquals() {
	int[] a = new int[] { 1, 2, 3 };
	int[] b = new int[] { 34, 21, 89 };

	boolean isEqual = Objects.deepEquals(a, b);
	Assert.assertFalse(isEqual);
}

Example 5: First parameter is int[] and second parameter is long[], hence deepEquals returns false.

@Test
public void deepEquals() {
	int[] a = new int[] { 1, 2, 3 };
	long[] b = new long[] { 1, 2, 3 };

	boolean isEqual = Objects.deepEquals(a, b);
	Assert.assertFalse(isEqual);
}

Example 6: Both parameters are int[][] and their values are the same, hence deepEquals returns true.

@Test
public void deepEquals() {
	int[][] a = new int[][] { { 1, 2, 3 }, { 1, 2, 3 } };
	int[][] b = new int[][] { { 1, 2, 3 }, { 1, 2, 3 } };

	boolean isEqual = Objects.deepEquals(a, b);
	Assert.assertTrue(isEqual);
}

Example 7: Both parameters are Object[] and their values are the same, hence deepEquals returns true.

@Test
public void deepEquals() {
	Object[] a = new Object[] { 1, 2, 3, new Object[] { 1, 2 } };
	Object[] b = new Object[] { 1, 2, 3, new Object[] { 1, 2 } };

	boolean isEqual = Objects.deepEquals(a, b);
	Assert.assertTrue(isEqual);
}

5. public static int hashCode(Object o)

hashCode(Object o) returns the hashCode of the object passed in parameter. It returns 0 if the object is null, else returns the value of o.hashCode() method call.

@Test
public void hashCodeTest() {
	int hash = Objects.hashCode(null);
	Assert.assertEquals(0, hash);

	int hash = Objects.hashCode(Integer.valueOf(42));
	Assert.assertTrue(hash != 0);
}

6. public static int hash(Object… values)

hash(Object… values) method is used to return combined hash code of all the values of varargs. If the varargs is null it returns 0. Else it returns combined hash code using below code.

int result = 1;

for (Object element : a)
	result = 31 * result + (element == null ? 0 : element.hashCode());

Example 1 : The varargs is null, hence the hash method returns 0.

@Test
public void hash() {
	int hash = Objects.hash(null);
	Assert.assertEquals(0, hash);
}

Example 2 : The varargs has 2 elements i.e. null and null, hence the hash method returns 961.

Why 961 and not 0? Because there is a check that says that if the input itself is null then only the result is 0. In this example our input is not null but the elements are null. Look at the hash generation code.

if (a == null)
            return 0;

int result = 1;

for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());

@Test
public void hash() {
	int hash = Objects.hash(null, null);
	Assert.assertEquals(961, hash);
}

Example 3 : The varargs contains Integer, String and Float objects. The hash returned will be an addition of all individual hashes.

@Test
public void hash() {
	Integer iVal = Integer.valueOf(42);
	String str = "Jon";
	Float fVal = Float.valueOf(3.14f);

	int hash = Objects.hash(iVal, str, fVal);
	Assert.assertTrue(hash != 0);
}

7. public static String toString(Object o)

toString method provides a way to convert an object to String. If the input is null then the “null” string is returned. If the input is not null then the result of o.toString() is returned.

@Test
public void toStringTest() {
	String str = Objects.toString(null);
	Assert.assertEquals("null", str);

	String str = Objects.toString(Long.valueOf(123456789123456789L));
	Assert.assertEquals("123456789123456789", str);

	String str = Objects.toString(Name.of("Jon", "Snow"));
	// Name [firstName=Jon, lastName=Snow]
	System.out.println(str);
}

8. public static
String toString(Object o, String nullDefault)

toString(Object o, String nullDefault) is a variant of the previous method i.e. toString(Object o).

If the input is null, then nullDefault is returned. Else it returns result of o.toString().

@Test
public void toStringDefault() {
	String nullDefault = "empty";
	String str = Objects.toString(null, nullDefault);
	Assert.assertEquals("empty", str);

	String nullDefault = "empty";
	String str = Objects.toString(Long.valueOf(16789L), nullDefault);
	Assert.assertEquals("16789", str);
}

9. public static <T> int
compare(T a, T b, Comparator<? super T> c)

compare(T a, T b, Comparator<? super T> c) is used to compare two objects of same type using a provided Comparator. 

  1. Returns 0, if both the objects are identical using == or both are null.
  2. Returns 0, if both the objects have the same ordering as per Comparator.
  3. Returns >= 1 if the first object’s order is higher than the second object or first object is greater than the second object.
  4. Returns <= -1 if first object’s order is lower than second object or first object is lesser than second object.

For example: We want to compare a Name. Name class contains firstName and lastName attributes.

final Comparator<Name> NAME_COMP = new Comparator<Name>() {
	@Override
	public int compare(Name n1, Name n2) {
		int result = n1.firstName().compareTo(n2.firstName());
		if (result != 0) {
			result = n1.lastName().compareTo(n2.lastName());
		}
		return result;
	}
};

I can write the same comparator using Java 8 as below. I have written an article on Complete guide to Comparator interface with 17 methods and over 60 examples.

Example 1: Both the objects are the same, so the result of comparison is 0.

@Test
public void compare() {
	Comparator<Name> firstNameComp = 
			(n1, n2) -> n1.firstName().compareTo(n2.firstName());

	Comparator<Name> lastNameComp = 
			(n1, n2) -> n1.lastName().compareTo(n2.lastName());

	Comparator<Name> nameComp = 
			firstNameComp.thenComparing(lastNameComp);

	Name n1 = Name.of("Brandon", "Stark");
	Name n2 = Name.of("Brandon", "Stark");

	int result = Objects.compare(n1, n2, nameComp);
	Assert.assertEquals(0, result);
}

Example 2 : 

@Test
public void compare() {
	Comparator<Name> firstNameComp = 
			(n1, n2) -> n1.firstName().compareTo(n2.firstName());
	Comparator<Name> lastNameComp = 
			(n1, n2) -> n1.lastName().compareTo(n2.lastName());

	Comparator<Name> nameComp = 
			firstNameComp.thenComparing(lastNameComp);

	Name n1 = Name.of("Brandon", "Stark");
	Name n2 = Name.of("Brandon", "Greyjoy");

	// firstName comparison returns 0
	// hence lastName comparison comes
	// into effect.
	// lastName "Stark" comes after "Greyjob" in lexical order hence 
	// "Stark" is greater than "Greyjoy"
	int result = Objects.compare(n1, n2, nameComp);
	Assert.assertTrue(result > 0);

	// firstName comparison returns 0
	// hence lastName comparison comes
	// into effect.
	// lastName "Greyjob" comes after "Stark" in lexical order hence 
	// "Greyjoy" is smaller than "Stark"
	int resultReverse = Objects.compare(n2, n1, nameComp);
	Assert.assertTrue(resultReverse < 0);
}

10. public static <T> T requireNonNull(T obj)

requireNonNull(T obj) method is used to do parametric validations for objects. This method would throw a NullPointerException if the input is null. If input is not null, it just returns the input as a result.

@Test(expected = NullPointerException.class)
public void requireNonNull1() {
	Objects.requireNonNull(null);
}

You can use this method like this : 

public static Name of(final String firstName, final String lastName) {
	Objects.requireNonNull(firstName);
	Objects.requireNonNull(lastName)
	return new Name(firstName, lastName);
}
@Test
public void requireNonNull() {
	Name name = Objects.requireNonNull(Name.of("Jon", "Snow"));
	Assert.assertNotNull(name);
}

11. public static <T>
T requireNonNull(T obj, String message)

requireNonNull(T obj, String message) is a variant of requireNonNull(T obj).

This method allows us to provide a custom message while throwing NullPointerException. 

@Test
public void requireNonNull() {
	String message = "null parameter is disallowed";
	try {
		Objects.requireNonNull(null, message);
	} catch (NullPointerException npe) {
		Assert.assertEquals(message, npe.getMessage());
	}
}

12. public static boolean isNull(Object obj)

isNull(Object obj) method returns boolean suggesting if the input is null. If the input is null then isNull method returns true, else returns false.

@Test
public void isNull() {
	String input = null;

	boolean isNull = Objects.isNull(input);
	Assert.assertTrue(isNull);
}

isNull method can be target for lambda expression and as method reference too.

@Test
public void isNull() {
	String input = null;

	Predicate<String> pred1 = (str) -> Objects.isNull(str);
	boolean isNull1 = pred1.test(input);
	Assert.assertTrue(isNull1);

	Predicate<String> pred 2= Objects::isNull;
	boolean isNull2 = pred2.test(input);
	Assert.assertTrue(isNull2);
}

Count the number of null elements using Stream.

@Test
public void isNull() {
	long count = Stream
					.of("a", "b", null, "c")
					.filter(Objects::isNull)
					.count();

	Assert.assertEquals(1, count);
}

Check if any element is null using Stream.

@Test
public void isNull() {
	boolean isNull = Stream
						.of("a", "b", null, "c")
						.anyMatch(Objects::isNull);

	Assert.assertTrue(isNull);
}

13. public static boolean nonNull(Object obj)

nonNull(Object obj) method returns boolean suggesting that input is not null. If the input is not null it returns true else returns false.

@Test
public void nonNull() {
	String input = "abc";
	
	boolean isNonNull = Objects.nonNull(input);
	Assert.assertTrue(isNonNull);
}

nonNull method can be target for lambda expression and as method reference too. 

@Test
public void nonNull() {
	String input = "abc";

	Predicate<String> pred1 = (str) -> Objects.nonNull(str);
	boolean isNonNull1 = pred1.test(input);
	Assert.assertTrue(isNonNull1);

	Predicate<String> pred2 = Objects::nonNull;
	boolean isNonNull2 = pred2.test(input);
	Assert.assertTrue(isNonNull2);
}

Count the number of non-null elements using Stream.

@Test
public void nonNull() {
	long count = Stream
					.of("a", "b", null, "c")
					.filter(Objects::nonNull)
					.count();

	Assert.assertEquals(3, count);
}

Print non-null elements using Stream.

@Test
public void nonNull() {
	Stream
		.of("a", "b", null, "c")
		.filter(Objects::nonNull)
		.forEach(System.out::println);
}

Collect non null elements using Collector.

@Test
public void nonNull() {
	List<String> result = Stream
							.of("a", "b", null, "c")
							.filter(Objects::nonNull)
							.collect(Collectors.toList());
}

14. public static <T>
T requireNonNullElse(T obj, T defaultObj)

requireNonNullElse(T obj, T defaultObj) method is used to return defaultObj if the input object obj is null. If input object obj and default object defaultObj, both of them, are null then the method throws NullPointerException.

Example 1: Provide default Name with first and last name as “default”.

@Test
public void requireNonNullElse() {
	Name input = Name.of("Jon", "Snow");
	Name defaultName = Name.of("default", "default");

	Name result = Objects.requireNonNullElse(input, defaultName);
	Assert.assertEquals(input, result);
}

Example 2: Provide default date as LocalDate.

@Test
public void requireNonNullElse() {
	LocalDate input = null;
	LocalDate now = LocalDate.now();

	LocalDate result = Objects.requireNonNullElse(input, now);
	Assert.assertEquals(now, result);
}

Example 3: Input is null and defaultObj is not null, it returns defaultObj.

@Test
public void requireNonNullElse() {
	Name input = null;
	Name defaultName = Name.of("default", "default");

	Name result = Objects.requireNonNullElse(input, defaultName);
	Assert.assertEquals(defaultName, result);
}

Example 4: Input and defaultObj, both of them, are null so it throws NullPointerException.

@Test
public void requireNonNullElse() {
	Name input = null;
	Name defaultName = null;
	try {
		Objects.requireNonNullElse(input, defaultName);
		Assert.fail();
	} catch (NullPointerException npe) {
		Assert.assertEquals("defaultObj", npe.getMessage());
	}
}

15. public static <T> T requireNonNullElseGet(
T obj, Supplier<? extends T> supplier)

requireNonNullElseGet(T obj, Supplier<? extends T> supplier) provides a Supplier of defaultObj. The actual object is created only if the input obj is null. This means that defaultObj creation is deferred until needed.

This method will throw a NullPointerException if both the parameters are null. It will also throw NullPointerException if Supplier returns null.

Example 1: Provide a Supplier of defaultObj. The Supplier won’t create a Name object if the input obj is not null.

@Test
public void requireNonNullElse() {
	Name input = Name.of("Jon", "Snow");
	Supplier<Name> defaultSupplier = 
				() -> Name.of("default", "default");

	Name result = Objects.requireNonNullElseGet(input, defaultSupplier);
	Assert.assertEquals(input, result);
}

Example 2: Input obj is null. Here the new Name obj will be created by Supplier.

@Test
public void requireNonNullElse() {
	Name input = null;
	Supplier<Name> defaultSupplier = 
				() -> Name.of("default", "default");

	Name result = Objects.requireNonNullElseGet(input, defaultSupplier);
	Assert.assertEquals(Name.of("default", "default"), result);
}

Example 3: Input obj and Supplier, both of them, are null. 

@Test
public void requireNonNullElse() {
	Name input = null;
	Supplier<Name> defaultSupplier = null;
	try {
		Objects.requireNonNullElseGet(input, defaultSupplier);
	} catch (NullPointerException npe) {
		Assert.assertEquals("supplier", npe.getMessage());
	}
}

Example 4: Input obj is null, but Supplier is present. Supplier returns null. This results is a NullPointerException.

@Test
public void requireNonNullElse() {
	Name input = null;
	Supplier<Name> defaultSupplier = () -> null;
	try {
		Objects.requireNonNullElseGet(input, defaultSupplier);
	} catch (NullPointerException npe) {
		Assert.assertEquals("supplier.get()", npe.getMessage());
	}
}

16. public static <T> T
requireNonNull(T obj, Supplier<String> messageSupplier)

requireNonNull(T obj, Supplier<String> messageSupplier) is used to check if input obj is null. If it is null it throws NullPointerException. The message for NullPointerException will be provided by Supplier<String> messageSupplier. If the obj is null then only the Supplier creates the message. Until then it does not create message.

public class Name {

	private final String firstName;
	private final String lastName;

	private Name(final String firstName, final String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	/**
	 * Static factory that creates new Name with firstName and lastName.
	 * 
	 * @param firstName
	 * @param lastName
	 * 
	 * @throws NullPointerException
	 *             if firstName or lastName is null
	 * 
	 */
	public static Name of(final String firstName, final String lastName) {
		Objects.requireNonNull(firstName);
		Objects.requireNonNull(lastName);
		return new Name(firstName, lastName);
	}

}


Example 1: Input obj is null. The exception message will is provided by Supplier<String>.

@Test
public void requireNonNullElse() {
	Supplier<String> messageSupplier = 
				() -> "null parameter is disallowed";
	try {
		Objects.requireNonNull(null, messageSupplier);
		Assert.fail();
	} catch (NullPointerException npe) {
		Assert.assertEquals(messageSupplier.get(), npe.getMessage());
	}
}

Example 1: Input obj is not null. As the input obj is not null method will return the input obj. 

@Test
public void requireNonNullElse() {
	Supplier<String> messageSupplier = 
				() -> "null parameter is disallowed";

	Name input = Name.of("Jon", "Snow");

	Name name = Objects.requireNonNull(input, messageSupplier);
	Assert.assertEquals(input, name);
}

17. public static
    int checkIndex(int index, int length)

checkIndex(int index, int length) method is used to check the bounds using index and length provided in the input parameter. 

This method will throw IndexOutOfBoundsException if either of the below three conditions is true : 

  1. index < 0
  2. length < 0
  3. index >= length

This means that index must be >=0 and < length.

If the index is within bounds, then it returns the index.

Example 1: The index is within the length of the array.

@Test
public void checkIndex1() {
	int[] arr = { 4, 54, 8, 5 };

	int result = Objects.checkIndex(2, arr.length);
	Assert.assertEquals(2, result);
}

Example 2: The index is negative, hence the method throws IndexOutOfBoundsException.

@Test(expected = IndexOutOfBoundsException.class)
public void checkIndex2() {
	int[] arr = { 4, 54, 8, 5 };
	Objects.checkIndex(-1, arr.length);
}

Example 3: The index is equal to length, hence the method throws IndexOutOfBoundsException.

@Test(expected = IndexOutOfBoundsException.class)
public void checkIndex3() {
	int[] arr = { 4, 54, 8, 5 };
	Objects.checkIndex(arr.length, arr.length);
}

Example 3: The index greater than length, hence the method throws IndexOutOfBoundsException.

@Test(expected = IndexOutOfBoundsException.class)
public void checkIndex4() {
	int[] arr = { 4, 54, 8, 5 };
	Objects.checkIndex(20, arr.length);
}

18. public static
    int checkFromToIndex(int fromIndex, int toIndex, int length)

checkFromToIndex(int fromIndex, int toIndex, int length) checks whether the subrange fromIndex(inclusive) to toIndex(exclusive) is within range 0 to length or not.

If the above condition is true, then the method returns fromIndex as a result. 

This method will throw IndexOutOfBoundsException if any of below condition is true : 

  1. fromIndex < 0 or toIndex < 0 or length < 0
  2. fromIndex > toIndex
  3. toIndex > length

Below are 8 examples : 

@Test
public void checkFromToIndex1() {
	int[] arr = { 4, 54, 8, 5 };

	int result = Objects.checkFromToIndex(0, 3, arr.length);
	Assert.assertEquals(0, result);
}

@Test
public void checkFromToIndex2() {
	int[] arr = { 4, 54, 8, 5 };

	int result = Objects.checkFromToIndex(0, 0, arr.length);
	Assert.assertEquals(0, result);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromToIndex3() {
	int[] arr = { 4, 54, 8, 5 };
	Objects.checkFromToIndex(0, 20, arr.length);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromToIndex4() {
	int[] arr = { 4, 54, 8, 5 };
	Objects.checkFromToIndex(3, 0, arr.length);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromToIndex5() {
	int[] arr = { 4, 54, 8, 5 };
	Objects.checkFromToIndex(0, 3, -1);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromToIndex6() {
	Objects.checkFromToIndex(-1, 3, 4);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromToIndex7() {
	Objects.checkFromToIndex(1, -3, 4);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromToIndex8() {
	Objects.checkFromToIndex(1, 3, -1);
}

19. public static
    int checkFromIndexSize(int fromIndex, int size, int length)

checkFromIndexSize(int fromIndex, int size, int length) is used to check for subrange from fromIndex(inclusive) to fromIndex + size(exclusive).

If the fromIndex + size < length then the method returns fromIndex.

This method will throw IndexOutOfBoundsException if any of the below condition is true : 

  1. fromIndex < 0 or size < 0 or length < 0
  2. fromIndex + size > length

Below are 5 examples: 

@Test
public void checkFromIndexSize1() {
	int[] arr = { 4, 54, 8, 5, 62, 84, 535 };

	int fromIndex = Objects.checkFromIndexSize(0, 5, arr.length);
	Assert.assertEquals(0, fromIndex);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromIndexSize2() {
	int[] arr = { 4, 54, 8, 5, 62, 84, 535 };
	Objects.checkFromIndexSize(-1, 5, arr.length);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromIndexSize3() {
	int[] arr = { 4, 54, 8, 5, 62, 84, 535 };
	Objects.checkFromIndexSize(1, -5, arr.length);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromIndexSize4() {
	int[] arr = { 4, 54, 8, 5, 62, 84, 535 };
	Objects.checkFromIndexSize(1, 5, -arr.length);
}

@Test(expected = IndexOutOfBoundsException.class)
public void checkFromIndexSize5() {
	int[] arr = { 4, 54, 8, 5, 62, 84, 535 };
	Objects.checkFromIndexSize(1, 15, arr.length);
}

20. Conclusion

That is all on Objects class and its 17 utility methods that are used for parameter null checks, toString, hashCode, equals and range based checks.

Leave a Reply

Your email address will not be published. Required fields are marked *