Thanks
Are you sure you've read the prohibition correctly? I can
think of no reason to avoid calling public methods in a clone()
implementation, although there are excellent reasons to avoid
calling overridable methods, the same reasons to avoid calling
them from constructors. Are you sure you haven't read "public"
where EJ actually says "non-final?"
--
Eric Sosman
eso...@ieee-dot-org.invalid
Yes, he refers to public (In clone chapter, 2nd edition), because, as
you said, they can be overridable.
What is the issue with that?
I only have the first edition; perhaps the text has changed.
Note, though, that public does not imply overridable, nor does
non-overridable imply non-public. They are different beasts:
public void method1() {
// public and overridable
}
public final void method2() {
// public, but not overridable
}
void method3() {
// non-public, but overridable
}
final void method4() {
// non-public and not overridable
}
private void method5() {
// non-public and not overridable
}
As method1() through method4() illustrate, access and overridability
are mostly independent. method5() shows why it's only "mostly:" the
method can't be overridden because subclasses can't even see it, much
less override it. (A subclass can define its own method5(), but it's
not an override: it's an entirely independent method. If the superclass
calls method5(), it always gets its own method5() and not the one in
the subclass.)
> What is the issue with that?
If an overridable method is called from a constructor or as part
of clone() or of deserialization, the code that runs might belong to
an overriding subclass and not to the superclass being dealt with at
the moment. This lets the subclass' code "see" its own superclass-ness
in an incompletely initialized state, a source of surprises that are
seldom pleasant. I think EJ discusses this point somewhere, but my
copy is not within reach at the moment.
class MyClonable implements Clonable {
int cloneCount;
public void setCloneCount(int cloneCount) {
this.cloneCount = cloneCount;
}
protected MyClonable clone() {
MyClonable myClone = (MyClonable)super.clone();
myClone.setCloneCount(value + 1);
}
}
class MyClonableBreakers extends MyClonable {
public void setCloneCount(int cloneCount) {
this.cloneCount = 0;
}
}
The contract of clone() that is given by MyClonable is broken by the
derived class, without touching the clone method.
This is obviously a contrived example, but it shows what could happen
(maliciously or accidentally).
--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>