Objects in Java 7

on waitingforcode.com

Objects in Java 7

You're still doing Java/C#/JavaScript/Python/PHP... and need a wind of change? I was like that 4 years ago. I changed then to the data engineering field and it solved my existential problems :) If you want to follow my path, I prepared a course that will help you with that! Join the class!
Java 7 was released in 2011 and brought to us, programmers, several interesting features as diamond operators or new I/O library. Yet another useful thing is less popular than two cited previously - Objects tool class.

In this article we'll present all features included in this static tool class. Roughly it's a great alternative for Objects class from Google Guava and Apache Commons builders (org.apache.commons.lang.builder) for equals() and hashCode() methods.

Before we start, there are common parts for each made tests:

public class ObjectsTest {

  private TestedObject tested1 = new TestedObject("Abc", 39);

    private static class TestedObject {

    private String name;

    private int age;

    public TestedObject(String name, int age) {
      this.name = name;
      this.age = age;
    }

    public String getName() {
      return this.name;
    }

    public Integer getAge() {
      return this.age;
    }

    @Override
    public int hashCode() {
      return Objects.hash(getName(), getAge());
    }

    @Override
    public boolean equals(Object compared) {
      if (!(compared instanceof TestedObject)) {
        return false;
      }
      TestedObject testedObject = (TestedObject) compared;
      return Objects.equals(getAge(), testedObject.getAge()) &&
              Objects.equals(getName(), testedObject.getName());
    }

    @Override
    public String toString() {
      return MoreObjects.toStringHelper(this).add("name", name).add("age", age).toString();
    }
  }

  private static class TestedObjectComparator implements Comparator<TestedObject> {

    @Override
    public int compare(TestedObject tested1, TestedObject tested2) {
      return ComparisonChain.start().compare(tested1.getName(), tested2.getName())
        .compare(tested1.getAge(), tested2.getAge())
        .result();
    }

  }
}

Comparing with Objects class

We'll begin by comparison of objects, as well through comparators as through equals() functions. Objects provides 3 methods which can help us to compare:

  • equals(Object a, Object b): it's used to check if two objects are equal.
  • deepEquals(Object a, Object b): as equals() but can be applied to "deep" objects as arrays.
  • compare(Object a, Object b, Comparator c): compares two objects using given comparator

There are some samples of these methods use:


  // equality cases
  @Test
  public void testEquality() {
    // To check equality with Objects.equals() method, compared objects must implement equals()
    // And in fact, the real utility of Objects.equals() method is inside implemented equals method where, to check
    // if two objects are equal, we very often compare its attributes.
    TestedObject tested1Prime = new TestedObject("Abc", 39);

    assertThat(Objects.equals(tested1, tested1Prime)).isTrue();
  }

  @Test
  public void testEqualityNotEquals() {
    TestedObject tested2 = new TestedObject("abc", 39);

    assertThat(Objects.equals(tested2, tested1)).isFalse();
  }

  @Test
  public void testEqualityForNullObjects() {
    // When one of compared Objects is null, we consider that both are not equal
    TestedObject tested3 = new TestedObject(null, 39);

    assertThat(Objects.equals(tested3, tested1)).isFalse();
  }

  @Test
  public void testEqualityForBothNullObjects() {
    TestedObject testedWithNull1 = new TestedObject(null, 39);
    TestedObject testedWithNull2 = new TestedObject(null, 39);

    assertThat(Objects.equals(testedWithNull1, testedWithNull2)).isTrue();
  }

  @Test
  public void testDeepEqualityForEqualArrays() {
    // deep equality test method is very useful for arrays; it uses Arrays.deepEquals method which, according to the
    // Javadoc http://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#deepEquals(java.lang.Object[],%20java.lang.Object[]),
    // checks the equality in several aspects
    TestedObject tested2 = new TestedObject("XYZ", 40);
    TestedObject tested3 = new TestedObject("JKL", 50);

    TestedObject[] testedArray1 = new TestedObject[] {tested1, tested2, tested3};
    TestedObject[] testedArray2 = new TestedObject[] {tested1, tested2, tested3};

    assertThat(Objects.deepEquals(testedArray1, testedArray2)).isTrue();
    assertThat(Objects.equals(testedArray1, testedArray2)).isFalse();
  }

  @Test
  public void testDeepEqualityForNotEqualArrays() {
    TestedObject tested2 = new TestedObject("XYZ", 40);
    TestedObject tested3 = new TestedObject("JKL", 50);

    TestedObject[] testedArray1 = new TestedObject[] {tested1, tested2, tested3};
    TestedObject[] testedArray2 = new TestedObject[] {tested1, tested2, null};

    assertThat(Objects.deepEquals(testedArray1, testedArray2)).isFalse();
  }

  @Test
  public void testDeepEqualityForNotTheSameLengthArrays() {
    TestedObject tested2 = new TestedObject("XYZ", 40);
    TestedObject tested3 = new TestedObject("JKL", 50);

    TestedObject[][] testedArray1 = new TestedObject[2][];
    testedArray1[0] = new TestedObject[] {tested1, tested2};
    testedArray1[1] = new TestedObject[] {tested2, tested3};

    TestedObject[][] testedArray2 = new TestedObject[2][];
    testedArray2[0] = new TestedObject[] {tested1};
    testedArray2[1] = new TestedObject[] {tested2};

    assertThat(Objects.deepEquals(testedArray1, testedArray2)).isFalse();
  }

  @Test
  public void testDeepEqualityForTheSameMultiDimsArrays() {
    TestedObject tested2 = new TestedObject("XYZ", 40);
    TestedObject tested3 = new TestedObject("JKL", 50);

    TestedObject[][] testedArray1 = new TestedObject[2][];
    testedArray1[0] = new TestedObject[] {tested1, tested2};
    testedArray1[1] = new TestedObject[] {tested2, tested3};

    TestedObject[][] testedArray2 = new TestedObject[2][];
    testedArray2[0] = new TestedObject[] {tested1, tested2};
    testedArray2[1] = new TestedObject[] {tested2, tested3};

    assertThat(Objects.deepEquals(testedArray1, testedArray2)).isTrue();
  }

  @Test
  public void testEqualityList() {
    List testedList1 = Arrays.asList("XYZ", "ABC");
    List testedList2 = Arrays.asList("XYZ", "ABC");

    assertThat(Objects.equals(testedList1, testedList2)).isTrue();
  }

  @Test
  public void testEqualityListNotEquals() {
    List testedList1 = Arrays.asList("123", "ABC");
    List<String> testedList2 = Arrays.asList("XYZ", "ABC");

    assertThat(Objects.equals(testedList1, testedList2)).isFalse();
  }

Generating hash codes with Objects class

Another features provided by Objects concern hash codes. Two methods were defined for them:

  • hash(Object... values): generates numeric value which can be used as hash code of given object.
  • hashCode(Object o): returns hash code of given object or 0 if the object is null.

Following test cases show how hash coding part can be used:

// hashCode cases
@Test
public void testHashCodeGeneration() {
  int hashCode123 = Objects.hash(3, "ABC", 123);
  int hashCode098 = Objects.hash(0, "ZYX", 980);
  int hashCode123Prime = Objects.hash(3, "ABC", 123);

  assertThat(hashCode123).isNotEqualTo(hashCode098);
  assertThat(hashCode123Prime).isEqualTo(hashCode123);
}

@Test
public void testHashCodingOnNullObject() {
  int nullHashCode = Objects.hashCode(null);

  assertThat(nullHashCode).isEqualTo(0);
}

@Test
public void testHashCodeOnNonNullObject() {
  int nonNullHashCode = Objects.hashCode(tested1);

  assertThat(nonNullHashCode).isGreaterThan(1);
}

Checking objects with Objects class

Another interesting point are requireNotNull methods which check if given object is not null. According to the Javadoc, they're "designed primarily for doing parameter validation in methods and constructs with multiple parameters". Every time when checked object is null, NullPointerException is thrown:

  • requireNonNull(T object): basic method which throws NPE.
  • requireNonNull(T object, String message): extended method which throws NPE with specified message.

These three test cases should help to understand the difference between two requireNonNull methods:

// object checking cases
@Test
public void testRequiringNotNull() {
  // requireNonNull methods will check if provided object is not null and throw NullPointerException when it's null
  TestedObject testedNotNull = Objects.requireNonNull(tested1);
  assertThat(testedNotNull).isEqualTo(tested1);
}

@Test(expected = NullPointerException.class)
public void testRequiringNotNullOnNullObject() {
  TestedObject nullTested = null;
  TestedObject testedNull = Objects.requireNonNull(nullTested);
}

@Test
public void testRequireNonNullWithMessage() {
  TestedObject nullTested = null;
  String realNpeMessage = "";
  String expectedMessage = "! Should not be null !";
  try {
      Objects.requireNonNull(null, expectedMessage);
  } catch (NullPointerException npe) {
      realNpeMessage = npe.getMessage();
  }

  assertThat(realNpeMessage).isEqualTo(expectedMessage);
}

String tools

The last utility part introduced by Objects concerns toString method:

  • toString(Object o): returns the result of toString() method of provided object. If the object is null, it returns a "null" String.
  • toString(Object o, String defaultMsg): as previous, but instead of returning "null" for null object, it returns the text specified in defaultMsg parameter.

We can observe that in following test cases:


In this article we can discover the interesting features defined in Objects class. Thanks to it, we can, for example, avoid some overhead associated with null checking, as well as with implementing of several Object methods (hashCode, toString, equals).

Share on: