Class 22

 

Last time: The compareTo method for object classes, and the resulting ability to sort those objects when they reside in an array or in an ArrayList.

Textbook coverage:  pp 648-658

This time: The equals method for object classes, and the resulting ability to use methods contains, indexOf, and lastIndexOf of ArrayList<ObjectClass> and get the expected results.

Textbook coverage: pp. 573-578

Equals header: public boolean equals(Object o)

First of all, we see a new type in the parameter of equals: Object. This is described on pg. 572.  Every object class IS-A Object, so Object is the ultimate super-type in Java.

The Object parameter of equals means that any object can be an argument of equals, not just an object of the same class as the equals method is in.

Example:

String s = “abc”;

String t = “xyz”;

String u = “abc”;

Integer i = 10;

s.equals(t) is false because s and t, both Strings, have different contents

s.equals(u) is true because s and u, both Strings, have the same contents

s.equals(i) is false because s and i, String vs. Integer, have different classes

Both equals and compareTo involve comparing two objects and deciding on their relationship, but we will see that the details are different, as can be first seen by looking at the headers for the methods:  Consider the object class String:

int compareTo(String s)   returns <0, 0 or >0 to indicate this String before, same, or after s in String order.

boolean equals(Object o)  returns true if o is also a String and this String has exactly the same contents as o’s String contents

                                                 returns false otherwise: i.e., if o is not a String, or it is a String with different contents.

For Integer:

int compareTo(Integer i)   returns <0, 0 or >0 to indicate this Integer before, same, or after i in Integer order.

boolean equals(Object o)  returns true if o is also an Integer and this Integer has exactly the same contents as o’s Integer contents

                                                 returns false otherwise: i.e., if o is not an Integer, or it is a Integer with different int value.

 

So we see that equals is required to decide on same-class relationship as well as same-contents relationship, whereas compareTo is guaranteed to receive a same-class object and only has to deal with the contents.

I believe the difference in approach is largely historical, not some deep difference in their use.  The parametric type used in the Comparable<T> interface was not available until Java 5, about 10 years after Java was born.

Equals is used in many JDK class’s methods. In ArrayList, it is used during the searches of indexOf, contains, and lastIndexOf, to check out each element.

Implementing equals: instanceof and casting

OK, so how in the world are we supposed to check if an object’s class is the same as the current class, the one whose equals method we are writing?

Of course we don’t have to worry about the equals code, or the compareTo code for String, Integer, etc.—these are all done for us, perfectly. Thus we can use indexOf for an ArrayList<String> without worry.

It’s the classes we write ourselves, like ITSystem, and Point in the text (We don’t need to worry about Point from the JDK). There we need to actually write the code.

In the book, pg. 576, we are introduced to the instanceof operator, which can be used to see if the classes are the same, for the classes we are using.

See the examples in Table 9.2, pg. 577.

With this operator, we can answer the question about the same-class relationship, but we aren’t done yet, because in the case that the class does match, we are still left with an Object o, and that Object reference cannot be used to call the methods of the class we are working in.

For example, see equals for Point on pg. 577.

The trick at this point is to cast the object to the class we are working in.  Point other = (Point)o;   (in equals for Point.java)

Then the resulting “other” reference is known to be of type Point, so it can be used to call Point’s methods and get to Point’s fields (from code inside Point.java).

So finally we can check that the Point’s x-values match and y-values match, and if so return true.

Another way to code that final decision is

  if (x == other.x && y == other.y) {

     return true;

  }

  else {

    return false;

  }

 

On pg. 578, a template is given so you can write an equals method for any class more or less automatically.

Here is the resulting code for ITSystem, where we say two systems are the same if they have the same id:

  // ITSystems are identified by id, so two objects

  // with the same id are considered equal.

  // This code is based on Point's equal, pg. 577.

  public boolean equals(Object o) {

    if (o instanceof ITSystem) {

      ITSystem other = (ITSystem)o;

      return id == other.id;   // check for matching id’s

    } else { // not an ITSystem object

      return false;

    }

  }

 

In general if a class has a unique id, matching ids is a good way to say two objects are equal.  If the class has no unique id, as in Point, you usually want to use all the fields in the determination of equality.  You see it is a software design decision.

The provided helper for project p3, to locate a system in the ArrayList<ITSystem> systems, is as follows:

private ITSystem findSystem(int id) {

    return systems.get(systems.indexOf(new ITSystem(id, 0, 0)));

}

 

This uses indexOf to search the ArrayList<ITSystem>, and thus is calling ITSystem’s equals method on each ITSytem object until it finds an “equals” one.

Here we can ask it to match against an object with only the id field really meaningful, because we know that equals will only use the id field in its work.

 

Next: Redo the weakOnes vs. privOnes example to use sorting, for faster code.  This corresponds to the final example in Chap. 10.

Then on to Sec. 13.1.