* Overriding equals * An extended class cannot use any new state in the implementation of the ``equals'' method. Let us say you have a ``Point'' class that contains an x and a y coordinate as integers. That class overrides ``equals'' and ``hashCode.'' You then decide to derive a ``PrettyPoint extends Point'' that adds a color. Should the ``PrettyPoint'' override the ``equals'' to consider the new color? A ``PrettyPoint'' may be used anywhere that a ``Point'' is expected. A plain ``Point'' may be compared to an extended ``PrettyPoint.'' Because of commutativity, we must guarantee the same result is returned from ``plainPoint.equals(prettyPoint)'' and ``prettyPoint.equals(plainPoint)''. The first method is not overridden and does not know about color. So a ``PrettyPoint'' clearly must ignore color when compared to a plain ``Point.'' Can a ``PrettyPoint'' use the color when compared to another ``PrettyPoint''? Transitivity requires that if ``point1.equals(point2)'' and ``point2.equals(point3)'', then ``point1.equals(point3)''. If ``point2'' is a plain ``Point'', then it does not matter if ``point1'' and ``point3'' are instances of ``PrettyPoint.'' The ``point1.equals(point3)'' must ignore the color to avoid breaking transitivity. There are a couple ways out of this dilemma. We could make ``Point'' extend ``PrettyPoint'' and make the extended ``Point'' colorless. The resulting hierarchies are unattractive and feel upside down. Each time we add a class with additional state, we must change all derived classes. This approach also does not work for a client attempting to extend third party code. Another approach is to make a ``PrettyPoint'' contain a ``Point'' with a method for getting that contained ``Point'' when necessary. This is best when all state is immutable. If ``Point'' is an interface rather than a concrete class, then each implementation of ``Point'' can insist that only objects from the same implementation be equal. Commutativity and associativity are preserved. Utility classes can define specific types of equality through the public methods of interfaces. The situation is the same for objects implementing ``Comparable.'' November, 2009 * Arrays * Arrays do not guarantee runtime type-safety. For example, the following code compiles without warnings, even with ``-Xlint:all''. => String[] strings = new String[1]; Object[] stuff = strings; stuff[0] = new Integer(13); <= There are no explicit casts, yet the code generates a ``java.lang.ArrayStoreException.'' This is the best reason for Java compiler to prohibit the creation of arrays of generic objects. All guarantees of type safety would be lost. This is also the reason that Java makes a distinction between a ``List'' and a ``List''. If we allowed a ``List'' to be passed as a ``List'' then the compiler would have no way to prevent a ``Double'' being added to the latter. November, 2009
Bill Harlan.