Avoid Incorrect Implementations and Bugs by Following There Guidelines
Summary
The Base Class for All Java Classes,
Java.lang.Object Allows Five of Its Methods To Be Overridden by Subclasses. Sometimes It Is Necessary To Override The Default IMPLEMENTATION
Object. Unfortunately, it's easy to override these methods incorrectly, which can result in classes that are difficult to subclass and that fail, in subtle ways, to work properly. This article explains why it's important to implement these methods correctly and also shows how to Override these Methods and get it right.
(3,500 Words)
By Mark Roulo
ll Java classes eventually have 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 can not be overridden. However, the other Methods in Object Can Be Andre Overridden, OFTEN INCORRECTLY. THIS ARTICLE EXPLAINS why it's impRectly and thrains 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 can not 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 Comparative SIMPLE TOSTRING AND FINALIZE METHODS.
Why this matters 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 can not 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 Program, YOURSELF RELELESING Your Library, Handling Excess Technical Support Calls, and Possibly Losing Sales As Customers Discover That Your Classes Can't Be EXT Ended.
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 IMPERLY IS The Least Damaging, But Can Still RESULT IN LOSS OF TIME, AS You Must Debug If.
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 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 THERE 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, The Equals Must Return False. When Overriding Equals, You Are Responsible For Enssuring That All The Specifics of The Contract Are STILL MET.
Implementing clone 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 CLIC 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 There Member 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 shouth you look?
Do not fail to implement the Cloneable interface if you want your class to be cloneable. The clone method from Object checks that the Cloneable interface has been implemented. If the Cloneable interface has not been implemented, a CloneNotSupportedException is thrown when clone is called . Do not implement clone by using a constructor The javadoc for the 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 can not override clone to make a deep Copy of the Member Variables in the childclass. The Following Code Demonstrates This Problem: Public Class ChildClass Extenship ate 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. summary: don '
t implement clone by using a copy constructor Avoid using constructors to copy subobjects when possible Another mistake is to use constructors to copy subobjects when implementing 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 sub-class of // Dimension, Any Data IN the sub-class (eg a think // Dimension Value Like Z) Will Be Lost. // IF (this.dim! = null) newObject.dim = new Dimension (DIM); return newobject;}}}} s 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 does not implement Cloneable in JDK1.1 and the clone method for Dimension is protected so Example can not 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 do not 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 clone NotSupportedException {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 aclone Object with the Wrong Data for Dim, But itst Still ISN '
ta good solution Summary:. Make copies of member variables using their clone methods if possible Pay attention to synchronization on the clone method 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. Sometimes you should treat clone like a constructor. Even though the clone method is not 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. Classes used by others should usually implement clone. This Is Most Important WHEN THE CLASS IS Part of a class library use by tase who don't have access to the source code. Failing to Implement The Clone Method Can Cause P roblems 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, do not 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. Child Classes Must Pay Attention To Clone Methods Inherited from Parent Classes. Well-Written Third-Party Library Classes Will Offlement 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 does not, you must override clone.Implementing equals and hashCode 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 the 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 Docunted in The Javadoc Documentation for Java.lang.Object:
The 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. If two objects are equal according to the equals method, they must return the same value from hashCode. The equals method is reflexive, which means that an object is equal to itself: x.equals (x) should return true The equals method is symmetric:. If x.equals (y) returns true, then y.equals (x) should return true also The equals method is transitive. : If x.equals (y) Returns true and y.Equals (z) Returns true, the x.equals (z) Should Return True. The equals method is consistent. X.equals (y) Should consistently return Either True Or False Java 2 Javadoc Clarifies That The Result of X.Equals (Y) CAN Change I .. F the information used in the equals comparisons change Finally, 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 (IE, 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 Hashcodefor 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 I 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 does not 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 do not 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 (ibject o) {ix ((Super.Equals (o) == true) && (O.getClass (). Equals (this.getClass ())))) rr ((ThreeDeedimension) o) .width == this.width; else returnaf 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 request (4) Is That Both test.}}}}}}}}}}} 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 will not return true when compared to objects of type Dimension You still have a problem with both Dimension and ThreeDeeDimension because they do not meet requirement (2):. Objects that test as equal should have identical hashCode values HOW IS Content-Equality Implement 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 will not 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 us inheriting Directly from a class thing // Overrode Equals, you 1000..insert a line here thing // LOOKED LIKE this: // IF (super.equals (o) == false) // Return False; ////iff overriding the EQ uals method from Object, do not // 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;} returnaf
} 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 this it // is an almost totally. // IT can 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. ///JA va 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 change,}} this importation meets all the required, including incruding request 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 Implement 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:
These are not simple methods to implement. There are many details specified in each method's contract. You must implement these two methods together. You can rarely implement just one of them. It is difficult to implement a correct, useful, and safe 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. you must pay attention to inheritance, especially when implementing equals. This means comparing classes with getClass rather than with instanceof. Once a class has overridden equals and hashCode, the child classes may also require their own implementations.
Implementing torstring 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 , havehcode 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 import information is not quite corrector. 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 Some Might Extend Baseclass and Might NOT OVERRIDE TOSTRING. AN EXAMPLE OF this is:
Public Class Extension Extenstructors, Member Data And Methods ...}
Now, Calling Tostring On Objects of Class Extension, 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 class (). GetName () "[" x "]";
Now Calling Tostring ON Objects of Class Extension Results in this Output:
Extension [4]
Which is correlectr.
Implementing Finalize There More Only Three Remenow 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 sol. Finalize Method Can Continue.
PROTECTED VOID FINALIZE () THROWS THROWABLE} Catch (throwable t) {} super.finalize ();
In General, Finalize Doesn't Need to Be Overridden.
Conclusion 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 can not easily fix things once the library has been released Failing to Implement the methodly 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 Theese Methods. In Those Cases You Should At Least Make Your Decision Consciously and Document IT.
About the authorMark 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.
Resources
Both the javadoc and the source code for the methods in java.lang.Object can be found at Sun's Java Web site http://java.sun.com The Eiffel programming language takes the notion of design-by-contract very seriously. This Paper Provides a Good Discussion of this Notion http://www.eiffel.com/doc/manuals/technology/contract/index.html
Shallow copying versus deep copying Shallow copying means that member variables are copied to the clone object while subobjects only have their references copied - so no new subobjects are created An example is:. Import java.awt.Dimension; public class Example implements Cloneable { private Dimension dim = new Dimension (); public synchronized Object clone () throws CloneNotSupportedException {return super.clone ();} public synchronized void setWidth (int width) {dim.width = width;} public synchronized int getWidth () {return dim.width;}} Creating an object of type Example and then cloning it will result in only one object of type Dimension, to be shared by both Example objects The example below shows this: public class main {public static void main (String. [] args) { Try {example E1 = new example (); system.out.println ("e1 width =" e1.GetWidth ()); example E2 = (example) E1.clone (); system.out.println ("E2 Width = " E2.Getwidth ()); System.out.Println (" Setting E1 Width To 10 "); E1.SetWidth (10); System.out.Println (" E1 Width = " E1.GetWidth ()) System.out.Println ("E2 Width =" E2.GetWidth ());} catch (cloneNotSupportedException Exception) {system.out.println ("OOOPS");