Personal tools
You are here: Home Developers Corner Java API documentation Java API toString, compareTo, equals, hashCode
Document Actions

Java API toString, compareTo, equals, hashCode

by Wayne Boucher last modified 2007-12-14 14:10

A discussion of the Java API implementation of toString(), compareTo(), equals() and hashCode(), and a comparison with how the Python API does it.

All Java objects automatically have toString(), equals() and hashCode() function, which can be overridden.  You can also add a compareTo() function, so as to implement the Comparable() interface.

The MemopsObject and MemopsDataTypeObject() versions are different.  For example, the former compare by key/id and the latter by value.  So we discuss them separately below.

Function signatures:

  •     int compareTo(Object other)
  •     int hashCode()
  •     boolean equals(Object other)

We want:

(e1.compareTo((Object)e2) == 0) should have the same boolean value as e1.equals((Object)e2), except that e1.comparesTo(null) throws a NullPointerException and e1.equals(null) returns false (if e1 is not itself null).

e1.equals(e2) implies that e1.hashCode() == e2.hashCode()

Below we write Id() instead of System.identityHashCode() to make the statements more succinct.

MemopsObject


The functions equals() and hashCode() are not overridden.

We only implement compareTo() for certain classes:

MemopsRoot:

  result = Id(this) - Id(other);

Other classes (MetaClass clazz, API class APIclass)

    The following determines whether we have a compareTo function:

    haveCompareTo = false
    parentRole = clazz.parentRole
    if parentRole:
      ot = parentRole.otherRole
      if inClass.keyNames or (ot is not None and ot.hicard == 1):
        if parentRole.container is clazz:
          haveCompareTo = True

If we have a compareTo function then the code is:

  if (other instanceof APIclass)

  {

      memops.api.Implementation.MemopsRoot p1 = getRoot();
      memops.api.Implementation.MemopsRoot p2 = other.getRoot();
      result = p1.compareTo(p2);
 
      if (result == 0) {
        List<Object> l1 = getFullKey();
        List<Object> l2 = other.getFullKey();
        Iterator<Object> iter1 = l1.iterator();
        Iterator<Object> iter2 = l2.iterator();

        while (iter1.hasNext()) {
          result = ((Comparable) iter1.next()).compareTo((Comparable) iter2.next());
          if (result != 0)
            break;
        }
      }
  }

  else

  {

    result = Id(this) - Id(other);

  }


This is compatible with the (default) equals() method, which compares on Id and if equal then on ==, because there can be only one MemopsRoot with a given Id and for other classes, there can only be one object with a given root and a given fullKey.

The Python API does not implement __equals__, __hash__ or __cmp__, so effectively uses Id/== in all cases.  This is functionally equivalent to the Java methods.


The toString() function is implemented as (leaving out some exception handling which obscures the code):

      String className = getQualifiedName();
      List<Object> key;
      key = getFullKey();

      StringBuffer ss = new StringBuffer("<");
      ss.append(className);
      if (getIsDeleted())
        ss.append(" (deleted)");

      ss.append(" [");

      if (key.isEmpty()) {
        key.add(hashCode());
        ss.append("id:");
      }

      int nn = 0;
      for (Object oo: key) {
        if (nn > 0)
          ss.append(", ");
        ss.append(oo);
        nn++;
      }
      ss.append("]>");

      result = ss.toString();

The Python API __repr__ should produce the same (at least to within differences in whitespace).

MemopsDataTypeObject


We override equals(), hashCode() and compareTo() for all non-abstract classes.

The code for equals() is:

    if (other == this) {
      result = true;
    } else if (other == null) {
      result = false;
    } else {
      int rr = compareTo(other);
      result = (rr == 0 ? true : false);
    }

The code for hashCode() is generated from the following (morally what is going on is that the attribute values determine the hashCode):

    elems = complexDataType.getAllAttributes()
    elems = [ elem for elem in elems if not elem.isDerived and not elem.isImplementation ]
    elems = metaUtil.sortByAttribute(elems, 'name')

    self.writeOne('result = 0;')

    for elem in elems:
      ss = uniUtil.upperFirst(elem.name)
      self.write("""
try {
  java.lang.Object oo = get%s();
  // below same idea as for java.util.List.hashCode()
""" % ss)

      if elem.locard == 0:
        self.write("""
  result = 31*result + (oo == null ? 0 : oo.hashCode());
""")
      else:
        self.write("""
  result = 31*result + oo.hashCode();
""")

      self.write("""
} catch (memops.general.ApiException aexc) {
}
""")

In the Python API there is no __equals__ implemented.

The code for compareTo() is generated from the following (morally what is going on is that attributes are compared):

      elems = complexDataType.getAllAttributes()
      elems = [ elem for elem in elems if not elem.isDerived and not elem.isImplementation ]
      elems = metaUtil.sortByAttribute(elems, 'name')

      tt = self.elementVarType(complexDataType)
      self.write("""
if (getClass().equals(other.getClass())) {
  result = 0;
  %s oo = (%s) other;
  while (true) { // only here because of break(s) below
""" % (tt, tt))

      nn = 0
      for elem in elems:
        if nn > 0:
          self.write("""
    if (result != 0)
      break;
""")

        ss = uniUtil.upperFirst(elem.name)
        tt = self.elementVarType(elem)

        if elem.hicard == 1:
          if elem.locard == 0:
            self.write("""
    try {
      %s vv = get%s();
      %s ww = oo.get%s();
      if ((vv == null) && (ww == null)) {
        // result = 0 still
      } else if ((vv == null) && (ww != null)) {
        result = -1;
      } else if ((vv != null) && (ww == null)) {
        result = 1;
      } else {
        result = vv.compareTo(ww);
      }
    } catch (memops.general.ApiException aexc) {
    }
""" % (tt, ss, tt, ss))

          else: # hicard = locard = 1
            self.write("""
    try {
      result = get%s().compareTo(oo.get%s());
    } catch (memops.general.ApiException aexc) {
    }
""" % (ss, ss))

        else: # hicard != 1
          if elem.isOrdered:
            ff = 'get'
          else:
            ff = 'sorted'
          self.write("""
    try {
      java.util.List<%s> o1 = %s%s();
      java.util.List<%s> o2 = oo.%s%s();
      result = o1.size() - o2.size();
      if (result != 0)
        break;

      java.util.Iterator<%s> iter1 = o1.iterator();
      java.util.Iterator<%s> iter2 = o2.iterator();
      while (iter1.hasNext()) {
          result = iter1.next().compareTo(iter2.next());
          if (result != 0)
            break;
      }
    } catch (memops.general.ApiException aexc) {
    }
""" % (tt, ff, ss, tt, ff, ss, tt, tt))

        nn = nn + 1

      self.write("""
    break;
  }
} else {
  result = java.lang.System.identityHashCode(this) - java.lang.System.identityHashCode(other);
}
""")

In the Python API __cmp__ also sorts on the attribute names but for hicard != 1 just uses the getter in all cases, whereas the Java uses the getter only if isOrdered=True, otherwise it uses the sorted function.

The code for toString() is generated from the following (morally what is going on is that the qualifiedName is shown and then the attributes):

      elems = complexDataType.getAllAttributes()
      elems = [ elem for elem in elems if not elem.isDerived and not elem.isImplementation ]
      elems = metaUtil.sortByAttribute(elems, 'name')

      self.writeOne('java.lang.StringBuffer ss = new java.lang.StringBuffer("<%s [");' % complexDataType.qualifiedName())

      nn = 0
      for elem in elems:
        if nn > 0:
          self.writeOne('ss.append(", ");')

        self.write('''
ss.append("%s");
ss.append("=");
ss.append("\\"");
try {
  ss.append(get%s().toString());
  ss.append("\\"");
} catch (memops.general.ApiException aexc) {
  int nn = ss.length();
  ss.deleteCharAt(nn-1);
  ss.append("??????");
} catch (java.lang.NullPointerException aexc) {
  int nn = ss.length();
  ss.deleteCharAt(nn-1);
  ss.append("null");
}
''' % (elem.name, uniUtil.upperFirst(elem.name)))

        nn = nn + 1

      self.writeOne('ss.append("]>");')
      self.writeOne('%s = ss.toString();' % self.varNames['result'])

The Python API __repr__ function is approximately the same as the above but the punctuation is different.


Powered by Plone, the Open Source Content Management System

This site conforms to the following standards: