java.lang.Object
, hereafter referred to simply as
Object
, as a base class. Because of this, all Java classes inherit methods from
Object
. Half of these methods are final and cannot be overridden. However, the other methods in
Object
can be and are overridden, often incorrectly. This article explains why it's important to implement these methods correctly and then explains how to do so.
Object
declares three versions of the wait
method, as well as the methods notify
, notifyAll
and getClass
. These methods all are final and cannot be overridden. This article discusses the remaining methods that are not final and that often must be overridden:
clone
toString
equals
hashCode
finalize
I'll discuss the clone
method first, as it provides a nice collection of subtle traps without being excessively complicated. Next, I'll consider equals
and hashCode
together. These are the most difficult to implement correctly. Wrapping up the article, I'll describe how to override the comparatively simple toString
and finalize
methods.
Why is it important to implement these methods correctly? In a small application written, used, and maintained by one individual, it may not be important. However, in large applications, in applications maintained by many people and in libraries intended for use by other people, failing to implement these methods correctly can result in classes that cannot be subclassed easily and that do not work as expected.
It is, for example, possible to write the clone
method so that no child classes can be cloned. This will be a problem for users who want to extend the class with the improperly written clone
method. For in-house development this mistake can result in excess debug time and rework when the problem is finally discovered. If the class is provided as part of a class library you sell to other programmers, you may find yourself rereleasing your library, handling excess technical support calls, and possibly losing sales as customers discover that your classes can't be extended.
Erroneous implementations of equals
and hashcode
can result in losing elements stored in hashtables. Incorrect implementation of these methods can also result in intermittent, data-dependent bugs as behavior changes over time. Again, this can result in excess debugging and extra software releases, technical support calls, and possibly lost sales. Implementing toString
improperly is the least damaging, but can still result in loss of time, as you must debug if the name of the object is wrong.
In short, implementing these methods incorrectly can make it difficult or impossible for other programmers to subclass and use the classes with the erroneous implementation. Less serious, but still important, implementing these methods incorrectly can result in time lost to debugging.
Two themes will reappear throughout this article. The first theme is that you must pay attention to whether your implementations of these methods will continue to be correct in child classes. If not, you should either rewrite your implementations to be correct in child classes or declare your class to be final so that there are no child classes.
The second theme is that methods have contracts -- defined behavior -- and when implementing or overriding a method, the contract should be fulfilled. The equals
method of Object
provides an example of a contract: the contract states that if the parameter to equals
is null
, then equals
must return false. When overriding equals
, you are responsible for ensuring that all the specifics of the contract are still met.
The clone
method allows clients to obtain a copy of a given object without knowing the precise class of the original object. The clone
method in Object
is a magic function that generates a shallow copy of the entire object being cloned.
To enable shallow cloning of your class, you implement the Cloneable
interface. (For a full discussion of shallow copying versus deep copying, see the sidebar below.) Since Cloneable
is a tagging interface with no methods, it's simple to implement:
public class BaseClass implements Cloneable { // Rest of the class.
// Notice that you don't even have to write the clone method! }
clone
is a protected method. If you want objects from other packages to be able to call it, you must make clone
public. You do this by redeclaring clone
and then calling the superclass's clone
method:
public class BaseClass implements Cloneable { // Rest of the class.
public Object clone () throws CloneNotSupportedException { return super.clone(); } }
Finally, if you want some of the member data in the class to be copied deeply, you must copy these members yourself:
public class BaseClass implements Cloneable { // SomeOtherClass is just an example. It might look like // this: // // class SomeOtherClass implements Cloneable // { // public Object clone () throws CloneNotSupportedException // { // return super.clone(); // } // } // private SomeOtherClass data;
// Rest of the class.
public Object clone () throws CloneNotSupportedException { BaseClass newObject = (BaseClass)super.clone();
// At this point, newObject shares the SomeOtherClass // object referred to by this.data with the object // running clone. If you want newObject to have its own // copy of data, you must clone this data yourself.
if (this.data != null) newObject.data = (SomeOtherClass)this.data.clone();
return newObject; } }
That's it. So, what mistakes should you look out for?
clone
method from Object
checks that the Cloneable
interface has been implemented. If the Cloneable
interface hasn't been implemented, a CloneNotSupportedException
is thrown when clone
is called.clone
method states that it:Creates a new object of the same class as this object. It then initializes each of the new object's fields by assigning it the same value as the corresponding field in this object. No constructor is called.
Notice that "no constructor is called." Avoid implementing clone
as follows:
public class BaseClass implements Cloneable { public BaseClass (/* parameters */) { // Code goes here... }
// Rest of the class.
public Object clone () throws CloneNotSupportedException { return new BaseClass (/* parameters */); } }
There are two reasons to avoid such an approach: First, the contract for clone
states that no constructor is called. Second, and more importantly, child classes now return the wrong type from clone
. In the example below, the object returned by clone
is a BaseClass
, not a ChildClass
!
public class ChildClass extends BaseClass { // Use clone from BaseClass }
Further, the child class cannot override clone
to make a deep copy of the member variables in the ChildClass
. The following code demonstrates this problem:
public class ChildClass extends BaseClass { private SomeOtherClass data;
// Rest of the class.
public Object clone () throws CloneNotSupportedException { // The cast in the line below throws an exception! // ChildClass newObject = (ChildClass)super.clone();
// You _never_ get here because the line above throws // an exception. if (this.data != null) newObject.data = (SomeOtherClass)this.data.clone();
return newObject; } }
The first line in clone
throws an exception because the clone
method in BaseClass
returns a BaseClass
object not a ChildClass
object.
Summary: Don't implement clone
by using a copy constructor.
clone
. Consider the following example class, which uses Dimension
as the subobject:import java.awt.Dimension;
public class Example implements Cloneable { private Dimension dim;
public void setDimension (Dimension dim) { this.dim = dim; }
public Object clone () throws CloneNotSupportedException { Example newObject = (Example)super.clone();
// Notice the use of a constructor below instead of // a clone method call. If you have a sub-class of // Dimension, any data in the sub-class (e.g. a third // dimension value like z) will be lost. // if (this.dim != null) newObject.dim = new Dimension (dim);
return newObject; } }
If a child class of Dimension
is passed to setDimension
, the object returned by clone
will be different from the original object. The preferred way to write this clone
method would be:
import java.awt.Dimension;
public class Example implements Cloneable { private Dimension dim;
public void setDimension (Dimension dim) { this.dim = dim; }
public Object clone () throws CloneNotSupportedException { Example newObject = (Example)super.clone();
// Call 'this.dim.clone()' instead of // 'new Dimension(dim)' // if (this.dim != null) newObject.dim = (Dimension)this.dim.clone();
return newObject; } }
Now, if a child class of Dimension
is passed to setDimension
, it is copied properly when clone
is called.
Unfortunately, while the preferred code above compiles under the Java 2 platform (formerly known as JDK 1.2), it won't compile under JDK 1.1.7. Dimension
doesn't implement Cloneable
in JDK1.1 and the clone
method for Dimension
is protected so Example
can't call it anyway. This means that under JDK 1.1 you must write Example
's clone
method using a copy constructor for the Dimension
member variable even though you don't want to. If a child of Dimension
is passed to setDimension
, you'll have a problem if you try to clone an Example
object.
Testing explicitly for Dimension
in the clone
method is one workaround:
import java.awt.Dimension;
public class Example implements Cloneable { private Dimension dim;
public void setDimension (Dimension dim) { this.dim = dim; }
public Object clone () throws CloneNotSupportedException { Example newObject = (Example)super.clone();
if (this.dim != null) { // Test explicitly for Dimension here. Don't test // using the instanceof operator -- it doesn't do // what you want it to. // if (this.dim.getClass() != Dimension.class) throw new CloneNotSupportedException("Wrong sub-class for 'dim'");
newObject.dim = new Dimension (dim); } return newObject; } }
This is better than returning a clone
object with the wrong data for dim
, but it still isn't a good solution.
Summary: Make copies of member variables using their clone
methods if possible.
clone
is a method just like any other. In a multithreaded environment you want to synchronize clone
so that the underlying object stays internally consistent while being copied. You must then also synchronize the mutator methods. Note that this is different from a constructor, which almost never needs synchronization.clone
method isn't a constructor, sometimes you should treat it like one. If you do something special in each constructor, like incrementing an "objects created" count, you probably want to do the same thing in the clone
method.clone
method can cause problems for clients attempting to write their own clone
methods -- see the problems with Dimension
in (2) above. If you're producing a third-party library, don't force your customers to work around a lack of cloning.If you're not producing a third-party library, waiting to implement clone
until it's needed for each class is reasonable. This is especially true because once you've overridden clone
, you must pay careful attention to overriding clone
in all the child classes.
clone
. However, once a class becomes cloneable, that class's children become cloneable, too. If you extend a class that is cloneable, you must consider whether the clone
method you inherit (which will make a shallow copy of all of the data in your subclass) does what you want it to. If it doesn't, you must override clone
.Because of their contracts, if you override either the equals
or hashCode
methods from Object
, you must almost certainly override the other method as well. The complicated contracts that go with these methods make overriding them correctly very difficult. Some of the code shipped with the standard Java libraries gets it wrong.
Here are the important contract requirements for the two methods, as documented in the javadoc documentation for java.lang.Object
:
hashCode
method must return the same integer value every time it is invoked on the same object during the entire execution of a Java application or applet. It need not return the same value for different runs of an application or applet. The Java 2 platform (Java 2) documentation further allows the hashCode
value to change if the information used in the equals
method changes.equals
method, they must return the same value from hashCode
.equals
method is reflexive, which means that an object is equal to itself: x.equals(x)
should return true.equals
method is symmetric: If x.equals(y)
returns true, then y.equals(x)
should return true also.equals
method is transitive: If x.equals(y)
returns true and y.equals(z)
returns true, then x.equals(z)
should return true.equals
method is consistent. x.equals(y)
should consistently return either true or false. The Java 2 javadoc clarifies that the result of x.equals(y)
can change if the information used in the equals comparisons change.x.equals(null)
should return false. Object
provides a simple implementation of equals
. It just tests the two objects for referential equality: does x equal y? Some of the standard Java classes override this to provide a more useful notion of equality -- usually content equality (i.e., is some or all of the data in the two objects identical?).
The equals
implementation of java.lang.String
, for example, returns true if the two objects are both String
objects containing exactly the same characters in exactly the same order. The equals method of java.awt.Dimension
returns true if the passed-in object is a dimension with the same width and height as the Dimension
object executing the equals
method.
The default implementation of hashCode
provided by Object
returns something corresponding to the object's address in memory or location in the Java virtual machine's global object array. Again, some of the standard Java classes override this method.
String
, for example, overrides the hashCode
implementation in Object
to return a hash of some or all of the characters making up the String
. This allows two String
objects with the same characters in the same order to return the same hash value. Dimension
uses the hashCode
method provided by Object
.
Now for the bad news: It's almost impossible to override equals
and hashCode
for mutable classes and provide useful, correct and safe implementations for both methods.
To see why, consider the class java.awt.Dimension
. This class overrides equals
, but not hashCode
. Dimension
's JDK 1.1 implementation of equals
looks like this:
public boolean equals(Object
obj) { if (obj instanceof Dimension) { Dimension d = (Dimension)obj; return (width == d.width) && (height == d.height); }
return false; }
This is a fairly reasonable implementation of content equality: if two Dimension
objects have the same width and height they're equal, otherwise they aren't. So, what's wrong?
The first problem is that because Dimension
doesn't override hashCode
, it's possible to have two Dimension
objects that are equal, but return different hashCode
values. This violates requirement (2) from above.
Second, testing the input parameter using instanceof Dimension
creates problems of its own. Consider a child class: ThreeDeeDimension
. Objects of type ThreeDeeDimension
should test as equal only if they have identical height, width and depth. ThreeDeeDimension
might look like this:
import java.awt.Dimension;
public class ThreeDeeDimension extends Dimension { // I don't like public data, but I'll make this public // to be similar to Dimension's width and height. // public int depth;
public ThreeDeeDimension (int width, int height, int depth) { super (width, height); this.depth = depth; }
public boolean equals (Object
o) { if ((super.equals (o) == true) &&(o.getClass().equals(this.getClass()))) return ((ThreeDeeDimension)o).width == this.width; else return false; } }
Unfortunately, this implementation of equals
doesn't meet requirement (4) listed above. The following code snippet shows this:
import java.awt.Dimension;
public class Main { static public void main (String[] args) { Dimension dim = new Dimension (1, 2); ThreeDeeDimension threeDeeDim = new ThreeDeeDimension (1, 2, 3);
// This will print out that the two objects are equal. System.out.println ("dim.equals(threeDeeDim) = " +dim.equals(threeDeeDim));
// And this will print out that the two objects are not equal. System.out.println ("threeDeeDim.equals(dim) = " +threeDeeDim.equals(dim));
// And requirement (4) is that both tests should return the // same result. } }
I can fix this problem by rewriting the equals
method of Dimension
. If I write equals
in Dimension
like this, the ThreeDeeDimension
class above meets requirement (4):
public boolean equals(Object obj) { if (obj != null && (obj.getClass().equals(this.getClass()))) { Dimension d = (Dimension)obj; return (width == d.width) && (height == d.height); }
return false; }
Now, objects of type ThreeDeeDimension
won't return true when compared to objects of type Dimension
. You still have a problem with both Dimension
and ThreeDeeDimension
because they don't meet requirement (2): objects that test as equal should have identical hashCode
values. So, how is content-equality implemented in mutable classes? One example with both equals
and hashCode
is:
public class MutableExample { private int x;
// Constructors and other methods.
public boolean equals (Object o) { // Test for null to meet requirement (7) and also to // avoid a NullPointerException when calling getClass() // below. // // Comparing classes ensures that parent class objects won't test // equal to child class objects. Parent objects should almost // never test equal to child objects. This makes meeting requirements // (4) and (5) possible. // if ((o != null) && (o.getClass().equals(this.getClass()))) { // If you were inheriting directly from a class that // overrode equals, you would insert a line here that // looked like this: // if (super.equals (o) == false) // return false; // // If overriding the equals method from Object, don't // call super.equals(). //
// This is the point. We have already tested the equality // of our parent class. Now we test for equality of the // data added by _this_ child class. This also meets requirement (3).
return ((MutableExample)o).x == this.x; }
return false; }
public int hashCode () { // This meets requirements (1) and (2). You always return the // same value for each object because you always return the // same value for all objects. You also return identical // hashCode values when two objects test as equals because // you always return identical hashCode values. There is no // requirement to return different hashCode values when // two objects test as not equal. // // The only real problem with this implementation is that it // is an almost totally useless implementation of hashCode. // It can turn a Hashtable lookup into a linear search. // // With JDK 1.1 you can't return 'x', because it can change // and the requirements for hashCode are that the same value // must be returned each time hashCode is called on the same object. // // Java 2 (formerly JDK 1.2) allows 'return x'; because Java 2 allows the hashCode // value to change if the underlying data changes. This is more // friendly, but still allows data to be lost in hashtables // if the underlying hashCode value changes. // return 0; } }
This implementation meets all the requirements, including requirement (6) with the clarification provided by Java 2.
Immutable classes make implementing a useful and safe hashCode
easier. In this case, you can use the data in the class to generate a hash value because that data will never change. In the example above, if "x" was guaranteed to never change, I could have implemented hashCode
like this:
public int hashCode() { // Legal, useful and safe, but only because 'x' never changes. // return x; }
The key points to remember when implementing equals
and hashCode
:
hashCode
method for mutable classes. Making classes immutable makes implementing the hashCode
and equals
methods much easier. Java 2 allows the value returned by hashCode
to change if the underlying data changes, but you should be wary of doing this because data can then be stranded in hashtables.equals
. This means comparing classes with getClass
rather than with instanceof
.equals
and hashCode
, the child classes may also require their own implementations.
The toString
method is the easiest of all the methods in Object
to override correctly. This is because the contract is so loosely defined. The javadoc for toString
reads:
[toString] returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.
The toString method for class
Object
returns a string consisting of the name of the class of which the object is an instance, the at-sign character `@', and the unsigned hexadecimal representation of the hash code of the object.
All of the above requirements are fuzzy. The method must return a "string representation" that in general "textually represents" the objects and should be "concise," but "informative." None of these requirements are as precise as the requirements for clone
, hashCode
or equals
. Nevertheless, it is still possible to implement this method somewhat incorrectly. Consider the following example:
public class BaseClass { private int x;
// Constructors and other member data and methods ...
public String toString() { // This implementation is not quite correct.
return "BaseClass[" + x + "]"; } }
Calling toString
on objects of this class will result in output that looks something like this (assuming x equals 4):
BaseClass[4]
The problem here is that someone might extend BaseClass
and might not override toString
. An example of this is:
public class Extension extends BaseClass { // Constructors, member data and methods ... }
Now, calling toString
on objects of class Extension
results in output that looks like this:
BaseClass[4]
The class name reported by toString
is wrong! The object is an Extension
object and the toString
method is reporting it as a BaseClass
object. You could blame the Extension
class for not implementing toString
itself, but the contract for toString
only recommends that child classes implement their own version. There is no requirement that child classes do so.
Instead, you should write toString
in BaseClass
so that it behaves correctly in child classes. You can do this by writing the toString
implementation like this:
public String toString() { // This implementation behaves properly in child classes.
return getClass().getName() + "[" + x + "]"; }
Now calling toString
on objects of class Extension
results in this output:
Extension[4]
which is correct.
There are only three relatively simple things to remember if you choose to override finalize
. First, you should call the finalize
method of the parent class in case it has cleanup to do.
protected void finalize() throws Throwable { super.finalize(); }
Second, you should not depend on the finalize
method being called. There is no guarantee of when (or if) objects will get garbage collected and thus no guarantee that the finalize
method will be called before the running program exits. Finally, remember that code in finalize
might fail and throw exceptions. You may want to catch these so the finalize
method can continue.
protected void finalize() throws Throwable { try { // Do stuff here to clean up your object. } catch (Throwable t) { }
super.finalize(); }
In general, finalize doesn't need to be overridden.
There are traps to overriding all of the nonfinal methods inherited from java.lang.Object
. Some of them are subtle enough that even classes provided in the core Java libraries get them wrong. Nevertheless, with a bit of care, you can implement the methods correctly.
When building large products and when constructing third-party class libraries, it is important to take the care needed to get these implementations right. After all, some developer might read the documentation and assume your code does what the documentation says. Failing to implement these methods correctly for large projects can result in extra time spent debugging. When implementing these methods in libraries sold commercially, it is even more important to implement these methods correctly; you cannot easily fix things once the library has been released. Failing to implement these methods properly can result in your library being harder to use and extend than it should be. Finally, for smaller projects, it can sometimes be reasonable to meet most, but not all, of the requirements for these methods. In those cases you should at least make your decision consciously and document it.
Mark Roulo has been programming professionally since 1989 and has been using Java since the alpha-3 release. He has been programming almost exclusively in Java for the past few years. His interests include portable, multithreaded, and distributed programming.