Embed
Email

clone

Document Sample

Categories
Tags
Stats
views:
1
posted:
11/19/2011
language:
English
pages:
10
Correctly override Object methods.

Object declares three versions of the wait method, as well as the methods notify,

notifyAll and getClass. These methods all are final and therefore cannot be

overridden. This article discusses how to override the non-final methods:



 clone

 toString

 equals

 hashCode

 finalize



Two themes



1. 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 them or

declare your class to be final.

2. Methods have contracts = specifications. The equals method of Object has a

contract that 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.



Implementing clone

The clone method allows clients to obtain a copy of a given object without knowing the

class of the original object. The clone method in Object generates a shallow copy of the

entire object being cloned. The following is pseudocode for it.



protected Object clone() throws CloneNotSupportedException

{ if (!(this instanceof Cloneable))

throw new CloneNotSupportedException();

Object klone = new ChunkOfMemoryOfTheSameSizeAs(this);

>

return klone;

}





To enable shallow cloning, implement the Cloneable interface. Since Cloneable is a tag

interface, this is trivial:



public class BaseClass implements Cloneable

{ // That's it! You don't even have to write the clone method.

}



However, clone is a protected method. If you want objects from other packages to be

able to call it, you must make it public: redeclare clone and call the superclass's clone

method:









1

public class BaseClass implements Cloneable

{ 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

{ private X data;



public Object clone () throws CloneNotSupportedException

{ BaseClass newObject = (BaseClass)super.clone();

// Now, newObject shares the X object referred to by this.data

// To have its own copy of data, you must clone it:

if (this.data != null)

newObject.data = (X) this.data.clone();

return newObject;

}

}



Mistakes look out for:



1. Implement the Cloneable interface if you want your class to be cloneable!

Object.clone checks that the Cloneable interface has been implemented. If not,

it throws a CloneNotSupportedException.

2. Don't implement clone by using a constructor. The doc. states that clone:



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.



Avoid implementing clone as follows:



public class BaseClass implements Cloneable

{ public Object clone () throws CloneNotSupportedException

{ return new BaseClass (this);

}

}



In other words: what was correct in C++ is incorrect in Java. 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 could 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

}







2

3. Avoid using constructors to copy subobjects. 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 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 subclass of

// Dimension, any data in the subclass (e.g. a third

// dimension value like z) will be lost.

if (this.dim != null)

newObject.dim = new Dimension(dim); //slice?

return newObject;

}

}



The preferred way to write this clone method would be:





public class Example implements Cloneable

{ private Dimension dim;



public Object clone () throws CloneNotSupportedException

{ Example newObject = (Example)super.clone();

if (this.dim != null)

newObject.dim = (Dimension) this.dim.clone();

return newObject;

}

}



Summary: Make copies of member variables using their clone methods.



4. Pay attention to synchronization on the clone method. In a multithreaded

environment you want to synchronize clone so that the underlying object stays

internally consistent while being copied. This is different from a constructor,

which almost never needs synchronization.

5. Sometimes you should treat clone like a constructor. 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.

6. Classes used by others should usually implement clone. This is most important

when the class is part of a class library used by others who don't have access to

the source code. Failing to implement the clone method can cause problems for

clients attempting to write their own clone methods..









3

If you're not producing a third-party library, waiting to implement clone until it's

needed is reasonable. This is especially true because once you've overridden

clone, you must pay attention to overriding clone in all the child classes.



7. Child classes must pay attention to clone methods inherited from parent

classes. Well-written third-party library classes will often implement 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.

8. Should you continue to have the exception specification "throws

CloneNotSupportedException"? Having this specification makes the client's

call to clone more awkward: they must be prepared to catch the exception. On the

other hand, if you omit this exception, then subclasses must supply a clone

operation: they are unable to retract from the contract. If the class is in a hierarchy

design to support cloning, you may reasonably omit the exception.



A hierarchy built for cloning.



Cloneable



public, no

exception

IBase specification



public clone()







"first" class

CW

data

no new data public clone() Immutable

means no data means no

override need to

override

CX CY

s : String







"second" class

CZ

data



public clone()



4

 Derive the base interface from Cloneable and set up clone() correctly:



public interface IBase extends Cloneable

{ public Object clone();

. . .

}



 "First" class's clone:



public Object clone()

{ CW copy = null;

try

{ copy = (CW) super.clone(); //Object.clone

}

catch(CloneNotSupportedException exc)

{ throw new InternalError(exc.getMessage());

} // ^^ an unchecked exception

>

return copy;

}



Because Object.clone may throw CloneNotSupportedException, we must either

catch it or propagate it.



 "Second" class's clone



public Object clone()

{ CZ copy = (CZ) super.clone(); //CX.clone

>

return copy;

}



Because CX.clone cannot throw CloneNotSupportedException, no try block!



 Immutable object data (ex: String, Color, Font) should be shared, not cloned

(these classes don't have a public clone, anyway).



//Example of a deep clone with a collection as a component



public class Third extends CZ

{ private ArrayList m_comps; //elements have type IBase



public Object clone()

{ Third klone = (Third) super.clone();

klone.m_comps = (ArrayList) m_comps.clone();

for(int i=0, ub = m_comps.size(); i
klone.m_comps.set(i,((IBase)m_comps.get(i)).clone());

return klone;

}

}









5

Implementing clone when you want to update final instance data



Is problematic! If you try to update final fields in your clone method, you are out of luck

– final fields can only be assigned in constructors. I suggest giving up! Write a C++ style

clone that simply returns a new copy, using the copy constructor. Note that this situation

is the case whenever you are trying to clone instances of non-static inner classes: the

parent pointer is final.



Implementing equals and hashCode

Because of their contracts, if you override one, you must override the other method as

well. Here are the important requirements, as documented for Object:



1. The hashCode method must return the same integer value every time it is

invoked. The documentation does allow the hashCode value to change if the

value of the equals method changes (the state changes).

2. If x.equals(y), they must return the same hashCode.

3. The equals is reflexive: x.equals(x) should return true.

4. The equals is symmetric: If x.equals(y), then y.equals(x).

5. The equals is transitive: If x.equals(y) and y.equals(z), then x.equals(z).

6. Finally, x.equals(null) should be false.



Object provides a simple implementation of equals. It tests the 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. The equals implementation of

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 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.



Here is Dimension’s implementation of equals. Spot the error.



public boolean equals(Object obj)

{ if (obj instanceof Dimension) 

{ Dimension d = (Dimension)obj;

return (width == d.width) && (height == d.height);

}

return false;

}









6

What’s wrong? Using instanceof creates problems of its own, if the class is not final.

Consider a derived class: ThreeDeeDim. Objects this type should be equal only if they

have equal height, width and depth. ThreeDeeDim might look like this:



public class ThreeDeeDim extends Dimension

{ public int depth;



public ThreeDeeDim (int width, int height, int depth)

{ super (width, height);

this.depth = depth;

}



public boolean equals (Object o)

{ if (super.equals(o)&& o.getClass().equals(this.getClass()))

return ((ThreeDeeDim)o).width == this.width;

else

return false;

}

}



Unfortunately, this implementation of equals doesn't meet the symmetry requirement

listed above. (Can you see why?) I could fix this problem by rewriting the equals

method of Dimension:



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 ThreeDeeDim won't return true when compared to objects of type

Dimension.



The key points to remember when implementing equals and hashCode:



 There are many details specified in each method's contract.

 You must implement these two methods together.

 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 hash tables. (On the other hand, if you mutate an object being used as a hash

table key, you deserve your mistake!)

 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.









7

Implementing toString

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.



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:



public class BaseClass

{ private int x;



public String toString()

{ return "BaseClass[" + x + "]"; // Not quite correct.

}

}



Calling toString on objects of this class will result in output that looks something like:



BaseClass[4]



The problem is that someone might extend BaseClass and but not override toString:



public class Extension extends BaseClass

{ /* no override of toString }



Now, calling toString on objects of class Extension results in output like:



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.. 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 + "]";

}







8

Implementing finalize

There are only three simple things to remember if you 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 – don't bother, in most cases.



One place where you may want to bother is if a class acquires a resource like a file

descriptor that needs to be released. This is what destructors are for in C++! In Java, the

best you can do is supply a close() method, say, and try to release the resource in

finalize(), if the user forgot to call close() explicitly:



public class InputFile

{ public class InputFile(. . .) throws IOException

{ /* open file */ }



public void close() throws IOException

{ m_in.close();

m_in = null;

}



protected void finalize() throws Throwable

{ try

{ if (m_in != null)

m_in.close();

}

catch(IOException e){}

super.finalize();

}



private InputStream m_in;

}









9

Conclusion

There are traps to overriding all of the non-final methods inherited from 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. And

won’t you be proud of yourself!



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! 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.









10



Related docs
Other docs by Stariya Js @ B...
How we become literate
Views: 0  |  Downloads: 0
15189
Views: 0  |  Downloads: 0
Enrollment Agreement
Views: 0  |  Downloads: 0
seddc 061009 pm
Views: 0  |  Downloads: 0
Juvanec-KamenNaKamen-eng
Views: 0  |  Downloads: 0
Syllabus Macro Fall 10
Views: 0  |  Downloads: 0
23401
Views: 0  |  Downloads: 0
9-11-RPH-stonefabrication-ord-memo-agss
Views: 0  |  Downloads: 0
Junior_Pre_season_Soccer_League_application
Views: 0  |  Downloads: 0
guide_to_moodle_quizzes
Views: 0  |  Downloads: 0
By registering with docstoc.com you agree to our
privacy policy

You are almost ready to download!

You are almost ready to download!