3.8. Important
As we've noted, all classes extend, directly or indirectly,
java.lang.Object
. This class defines several important methods that you should consider overriding in every class you write. Example 3-6 shows a class that
Some of the syntax in Example 3-6 may be unfamiliar to you. The example uses two Java 5.0 features. First, it implements a parameterized, or generic, version of the
Comparable
interface. Second, the example uses the
@Override
annotation to
// This class represents a circle with immutable position and radius.
public class Circle implements Comparable<Circle> {
// These fields hold the coordinates of the center and the radius.
// They are private for data encapsulation and final for immutability
private final int x, y, r;
// The basic constructor: initialize the fields to specified values
public Circle(int x, int y, int r) {
if (r < 0) throw new IllegalArgumentException("negative radius");
this.x = x; this.y = y; this.r = r;
}
// This is a "copy constructor"--a useful alternative to clone()
public Circle(Circle original) {
x = original.x; // Just copy the fields from the original
y = original.y;
r = original.r;
}
// Public accessor methods for the private fields.
// These are part of data encapsulation.
public int getX() { return x; }
public int getY() { return y; }
public int getR() { return r; }
// Return a string representation
@Override public String toString() {
return String.format("center=(%d,%d); radius=%d", x, y, r);
}
// Test for equality with another object
@Override public boolean equals(Object o) {
if (o == this) return true; // Identical references?
if (!(o instanceof Circle)) return false; // Correct type and non-null?
Circle that = (Circle) o; // Cast to our type
if (this.x == that.x && this.y == that.y && this.r == that.r)
return true; // If all fields match
else
return false; // If fields differ
}
// A hash code allows an object to be used in a hash table.
// Equal objects must have equal hash codes. Unequal objects are allowed
// to have equal hash codes as well, but we try to avoid that.
// We must override this method since we also override equals().
@Override public int hashCode() {
int result = 17; // This hash code algorithm from the book
result = 37*result + x; // _Effective Java_, by Joshua Bloch
result = 37*result + y;
result = 37*result + r;
return result;
}
// This method is defined by the Comparable interface.
// Compare this Circle to that Circle. Return a value < 0 if this < that.
// Return 0 if this == that. Return a value > 0 if this > that.
// Circles are ordered top to bottom, left to right, and then by radius
public int compareTo(Circle that) {
long result = that.y - this.y; // Smaller circles have bigger y values
if (result == 0) result = this.x - that.x; // If same compare l-to-r
if (result == 0) result = this.r - that.r; // If same compare radius
// We have to use a long value for subtraction because the differences
// between a large positive and large negative value could overflow
// an int. But we can't return the long, so return its sign as an int.
return Long.signum(result); // new in Java 5.0
}
}
The purpose of the
toString( )
method is to return a textual representation of an object. The method is invoked automatically on objects during string concatenation and by methods such as
System.out.println( )
. Giving objects a textual representation can be quite helpful for debugging or logging output, and a well-crafted
toString()
method can even help with
The version of
toString()
inherited from
Object
returns a string that includes the
The = = operator tests two references to see if they refer to the same object. If you want to test whether two distinct objects are equal to one another, you must use the equals() method instead. Any class can define its own notion of equality by overriding equals() . The Object.equals( ) method simply uses the == operator: this default method considers two objects equal only if they are actually the very same object.
The
equals( )
method in Example 3-6 considers two distinct
Circle
objects to be equal if their fields are all equal. Note that it first does a quick identity test with
= =
as an optimization and then checks the type of the other object with
instanceof
: a
Circle
can be equal only to another Circle, and it is not acceptable for an
equals()
method to throw a
ClassCastException
. Note that the
instanceof
test also rules out
null
arguments:
instanceof
always
Whenever you override
equals( )
, you must also override
hashCode( )
. This method returns an integer for use by hash table data structures. It is critical that two objects have the same hash code if they are equal according to the
equals()
method. It is important (for efficient operation of hash tables) but not required that unequal objects have unequal hash codes, or at least that unequal objects are
The Object.hashCode() method works with the Object.equals( ) method and returns a hash code based on object identity rather than object equality. (If you ever need an identity-based hash code, you can access the functionality of Object.hashCode() through the static method System.identityHashCode( ) .) When you override equals( ) , you must always override hashCode() to guarantee that equal objects have equal hash codes. Since the equals( ) method in Example 3-6 bases object equality on the values of the three fields, the hashCode( ) method computes its hash code based on these three fields as well. It is clear from the code that if two Circle objects have the same field values, they will have the same hash code.
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}
Note that the
hashCode( )
method in Example 3-6 does not simply add the three fields and return their sum. Such an implementation would be legal but not efficient because two circles with the same radius but whose X and Y coordinates were
Example 3-6 includes a
compareTo( )
method. This method is defined by the
java.lang.Comparable
interface rather than by
Object
. (It actually uses the generics features of Java 5.0 and implements a parameterized version of the interface:
Comparable<Circle>
, but we can ignore that fact until Chapter 4.) The purpose of
Comparable
and its
compareTo( )
method is to allow instances of a class to be compared to each other in the way that the
<
,
<=
,
>
and
>=
operators compare
Since
compareTo( )
is defined by an interface, the
Object
class does not provide any default implementation. It is up to each individual class to determine whether and how its instances should be ordered and to include a
compareTo()
method that implements that ordering. The ordering defined by Example 3-6
The compareTo( ) method returns an int value that requires further explanation. compareTo() should return a negative number if the this object is less than the object passed to it. It should return 0 if the two objects are equal. And compareTo() should return a positive number if this is greater than the method argument.
Object defines a method named clone() whose purpose is to return an object with fields set identically to those of the current object. This is an unusual method for two reasons. First, it works only if the class implements the java.lang.Cloneable interface. Cloneable does not define any methods, so implementing it is simply a matter of listing it in the implements clause of the class signature. The other unusual feature of clone() is that it is declared protected (see Section 3.6 earlier in this chapter). This means that subclasses of Object can call and override Object.clone() , but other code cannot call it. Therefore, if you want your object to be cloneable, you must implement Cloneable and override the clone() method, making it public .
The Circle class of Example 3-6 does not implement Cloneable ; instead it provides a copy constructor for making copies of Circle objects:
Circle original = new Circle(1, 2, 3); // regular constructor Circle copy = new Circle(original); // copy constructor
It can be difficult to implement clone( ) correctly, and it is usually easier and safer to provide a copy constructor. To make the Circle class cloneable, you would add Cloneable to the implements clause and add the following method to the class body:
@Override public Object clone() {
try { return super.clone(); }
catch(CloneNotSupportedException e) { throw new AssertionError(e); }
}
See Effective Java Programming Guide by Joshua Bloch for a detailed discussion of the ins and outs of clone() and Cloneable .