Why not cloneable by default?

J

John C. Bollinger

CLJA removed as not relevant to this branch of the discussion.
Does anyone know why the java designers didn't make every object
shallow-cloneable by default?

I understand the implications of cloning something overly hefty, and to be
cautious about such things, but it still seems a little overboard to me to
err on the side of turning if off.

Turning it off could easily have been accomplished by implementing something
called:

public interface NotCloneable {}

In fact, I might have allowed two methods, deep within Object:

public Object shallowClone()

and

public Object deepClone()

Now deep cloning is of course fraught with peril, and I am normally very
much in favor of the safety restrictions in the java language, but I'm not
sure this is the kind of thing that java should protect against.

So as long as we're having this discussion, perhaps we can come up with
a real design for a better Java cloning mechanism. (Then we just have
to build a time machine, and this whole thread will vanish :).) I
think, for those who are game, that we should start right at the
beginning, by defining the requirements and specifications. The
following are my own restatement of what I think are the key specs
behind Java's existing mechanism:

General Requirements

1) The cloning mechanism shall produce from an object a distinct object
of the same class that is logically a copy of the original.

2) The definition of "logically a copy" shall be controllable by
individual classes.

3) Classes shall be able to control whether or not their instances may
be cloned.

Specifics

4) A default cloning implementation shall be provided that performs a
field-by-field (shallow) copy of an object's state to initialize the clone.


Before we go on, then, the floor is open for discussion of the specs.
 
T

Thomas G. Marshall

John C. Bollinger coughed up:
I assert that at least sometimes, a class is fundamentally
inconsistent with cloning. As I responded to Thomas, a Singleton /
Multiton class would be an example (though true singletons and
multitons are admittedly rare). I also think that the barriers to
using clone() in Java make programmers put some thought into using
it, and that that's a good thing.

I'm trying to think on a general OO level here and to apply it to
Java, so I appreciate your Smalltalk-based observation. I'm not sure
that I'm happy giving much weight to negative anecdotal evidence like
that, however. Do you disagree with my claim that some classes are
inherently inconsistent with cloning?

/Some/ ? Well of course *I* the OP do, but /some/ isn't specifically the
issue. The issue was whether or not the default level of both compile-time
and run-time safety is worth the hassle, and as an aggregate whether or not
it helps or impedes the bottom line. The thrust of my question.

You were asking Chris, but this was a better place to address your points to
me.

And your worries about what is a disaster for cloning only scratches the
surface. There are many resource level things that just don't want multiple
objects banging on them: GUI elements, and i/o streams jump to mind.

This position of mine normally flies right in the face of what I've always
called the "golden handcuffs" of java. Which were virtues I've always
extolled beyond the normal pundit. And I'm not that sure of it either way,
hence the desire for this discussion.


--
Puzzle: You are given a deck of cards all face up
except for 10 cards mixed in which are face down.
If you are in a pitch black room, how do you divide
the deck into two piles (may be uneven) that each
contain the same number of face-up cards?
Answer (rot13): Sebz naljurer va gur qrpx, qrny bhg
gra pneqf naq syvc gurz bire.
 
T

Thomas G. Marshall

John C. Bollinger coughed up:
CLJA removed as not relevant to this branch of the discussion.


So as long as we're having this discussion, perhaps we can come up
with a real design for a better Java cloning mechanism. (Then we just
have
to build a time machine, and this whole thread will vanish :).) I
think, for those who are game, that we should start right at the
beginning, by defining the requirements and specifications. The
following are my own restatement of what I think are the key specs
behind Java's existing mechanism:

General Requirements

1) The cloning mechanism shall produce from an object a distinct
object of the same class that is logically a copy of the original.

2) The definition of "logically a copy" shall be controllable by
individual classes.

3) Classes shall be able to control whether or not their instances may
be cloned.

Specifics

4) A default cloning implementation shall be provided that performs a
field-by-field (shallow) copy of an object's state to initialize the
clone.

"bit for bit" (also shallow) makes me feel better----there may be hidden
state issues that we may (maybe not---hmmmmm) want.
 
A

Adam P. Jenkins

John said:
Thomas said:
John C. Bollinger coughed up:
[...] If you give me a good
reason why you should be enabled to invoke clone() at will on an
object of unknown type, then I'll freely admit that having to use
reflection to do so is indeed very unfortunate.



Imagine a special collection with a method that takes arbitrary
objects, but copies them before storing them internally. Such a
collection would be useful when you need to store a snapshot of an
object's state in a collection when that object's state is continually
in flux.


I acknowledge that such a collection might be useful, but I deny that
such a collection would be safe or appropriate to use in the general
case. Some classes' nature is simply inconsistent with cloning
(singletons / multitons, for instance). Many classes could be safely
cloned, but only if they cooperated. In your example, for instance, if
I want to preserve a state snapshot then the shallow clone provided by
Object.clone() is insufficient for mutable objects that hold references
to other mutable objects.

No, I remain convinced that Java's cloning mechanism is right to give
classes control of whether or not their instances can be cloned, and of
how. There is still plenty of room to debate the details, but I'm not
prepared to support a design that involves a general purpose,
clone-anything-you-want-to mechanism.

I think that last paragraph is a little bit of a straw man argument. I
don't think anyone in this discussion is saying that classes shouldn't
have control over whether their instances can be cloned. The discussion
is about *how* classes should control their ability to be cloned.

I agree with the original poster that it would be cleaner and more
consistent with OO design if the Cloneable interface actually contained
a clone() method, and you could simply cast Objects to Cloneable to
clone them. Classes can control whether or not they support cloning by
simply choosing whether or not to implement Cloneable. I understand why
Object needs to provide a clone() method -- because it contains magic
functionality which needs to be implemented by the system. However the
Cloneable interface could still have contained a public clone() method,
and in the simplest case its implementation would just delegate to the
protected Object.clone(). In fact the documentation for
java.lang.Cloneable even recommends doing this, so I really don't
understand why they didn't just add public clone() to the interface.

Adam
 
C

Chris Uppal

John said:
I assert that at least sometimes, a class is fundamentally inconsistent
with cloning.

Yes, I agree with that.

I'd go further and say that the meaning of 'copy' (I'd rather stay clear of the
word 'clone' which to me implies a specific implementation) is often vague, and
that to provide a copy operation is to accept a considerable semantic
responsibility.

What I don't agree with is that such cases are common enough that copy should
be unavailable by default.

I have more to say, but it'll take a bit of thought to organise, and I suspect
that it'll fit better in response to you newer post in this thread.

-- chris
 
T

Thomas G. Marshall

Adam P. Jenkins coughed up:

....[rip]...
I agree with the original poster that it would be cleaner and more
consistent with OO design if the Cloneable interface actually
contained a clone() method, and you could simply cast Objects to
Cloneable to clone them.

Not really what I was saying. I was saying that there should be a public
(Object).clone() usable by default, and when you need to disable it, you
"mark" your object by implementing Uncloneable. It would require the
compiler to treat Uncloneable as a special case built-in marker interface.

Classes can control whether or not they
support cloning by simply choosing whether or not to implement
Cloneable. I understand why Object needs to provide a clone() method
-- because it contains magic functionality which needs to be
implemented by the system.

Yes. Native methods or not, it's also the logical place since everything
descends from it.

However the Cloneable interface could
still have contained a public clone() method,

All method declarations within interfaces are enforced to be "public"
regardless.

and in the simplest
case its implementation would just delegate to the protected
Object.clone().

Which requires the user to actually implement the call to super.clone(),
which I'm not wild about as I stated originally.

In fact the documentation for java.lang.Cloneable
even recommends doing this, so I really don't understand why they
didn't just add public clone() to the interface.

They clearly wanted compile-time protection from calling the thing.
Requiring the implementation of Cloneable provides run-time protection. The
enforcing of your own clone() method (to call super.clone()) is their
compile-time protection.

Like I said---I think that it may have been overkill to have that by
default.
 
T

Tony Morris

John C. Bollinger said:
CLJA removed as not relevant to this branch of the discussion.


So as long as we're having this discussion, perhaps we can come up with
a real design for a better Java cloning mechanism. (Then we just have
to build a time machine, and this whole thread will vanish :).) I
think, for those who are game, that we should start right at the
beginning, by defining the requirements and specifications. The
following are my own restatement of what I think are the key specs
behind Java's existing mechanism:

General Requirements

1) The cloning mechanism shall produce from an object a distinct object
of the same class that is logically a copy of the original.

2) The definition of "logically a copy" shall be controllable by
individual classes.

3) Classes shall be able to control whether or not their instances may
be cloned.

Specifics

4) A default cloning implementation shall be provided that performs a
field-by-field (shallow) copy of an object's state to initialize the clone.


Before we go on, then, the floor is open for discussion of the specs.

I'm not sure if this is what you were meaning to say, but the only real
contract for implementers is specified by Object.clone().
To paraphrase the important parts:

x.clone() != x
x.clone().getClass() == x.getClass();
"typically the case, but not an absolute requirement":
x.clone().equals(x)
"By convention, the returned object should be obtained by calling
super.clone [sic super.clone()]
"By convention, the object returned by this method should be independent of
this object..."
etc.

How should we fix it? Here's one easy way:

package com.fixbrokenapis;
public interface Cloneable<T>{T clone();}

The same could be said for the equals/hashCode methods being better off
extracted to an interface - the consequences being less dire, but in my
opinion, still an atrocity as it currently (and always will) stands. A
practical example is the answer to the question, "how many times have you
seen a broken implementation?" I don't know about you, but I see it broken
more times than not.
....and if you are a "Nazi" (to quote my work colleagues), you would *always*
use an interface, *never* declare a class non-final, *never* expose
constructors (declared private), *never* expose the ability to mutate an
object through the interface, and always follow the TDD process
(encapsulation is paramount!). Of course, sometimes Nazis have to violate
their own rules - typically, it's to fit into some other framework that
mandates it.
It's just a matter of "where do *you* draw the line?". I argue that if you
don't draw the line at the level of a perceived "Nazi" (or somewhere
thereabouts), then you merely have more learning to do on the subject - not
to suggest that I too, don't indulge in the process of learning.
The existing clone mechanism is down the severe end of being broken, so
someone who is not a "Nazi" might consider reimplementing it.
</rant>
 
L

Lee Fesperman

John said:
So as long as we're having this discussion, perhaps we can come up with
a real design for a better Java cloning mechanism. (Then we just have
to build a time machine, and this whole thread will vanish :).) I
think, for those who are game, that we should start right at the
beginning, by defining the requirements and specifications. The
following are my own restatement of what I think are the key specs
behind Java's existing mechanism:

General Requirements

1) The cloning mechanism shall produce from an object a distinct object
of the same class that is logically a copy of the original.

2) The definition of "logically a copy" shall be controllable by
individual classes.

3) Classes shall be able to control whether or not their instances may
be cloned.

Specifics

4) A default cloning implementation shall be provided that performs a
field-by-field (shallow) copy of an object's state to initialize the clone.

Before we go on, then, the floor is open for discussion of the specs.

Thanks, John, though this may be an exercise in futulity we could at least submit it to
JCP (is there nothing at this point?)

Would you add an issues/questions section? My contribution would be to add Chris's about
shallow/deep cloning potentially being a consideration outside of the class
implementation ... while still being controlled by the class.
 
C

Chris Smith

Tony Morris said:
I'm not sure if this is what you were meaning to say, but the only real
contract for implementers is specified by Object.clone().

The text of the API documentation is normative when it intends to be,
but it is not a fundamentally reliable source for good practices. The
clone() method's API docs make very few normative statements -- only
that it's a "copy", and that the definition of "copy" depends on the
class of the object.

As for the "typical" statements from that document:
x.clone() != x
x.clone().getClass() == x.getClass();

These are, obviously, important.
"typically the case, but not an absolute requirement":
x.clone().equals(x)

I couldn't disagree more strongly with this one. If a class is
immutable, it should not be exposing clone(). If it is mutable, it
should not be overriding equals(Object). Since it was already mentioned
that x.clone() != x, the implementation of equals(Object) inherited from
the Object class will return false. I would, therefore, expect that
when good design is practiced, x.clone().equals(x) should return false.
How should we fix it? Here's one easy way:

package com.fixbrokenapis;
public interface Cloneable<T>{T clone();}

IMO, this is broken, too. The problem is that, for example, it's not
possible to implement *both* Cloneable<Shape> and Cloneable<Rectangle>.
So which one do you pick? The answer has to be Cloneable<Shape>, and
then you've lost what you're looking for in Rectangle.

Initially, this seems to be the same situation as with Comparable... but
it's really not. When a non-final type implements Comparable, it *must*
define in no uncertain terms what it means to compare any objects of
that type, including any subtypes. For example, java.util.List contains
a clear description of what makes two lists equal, and that description
is independent of which subclass is present. Therefore, an ArrayList
really CAN be compared to a LinkedList.

When cloning, the whole point is that if I call clone() on a Rectangle,
I will get back a Rectangle. If the class implements Comparable<Shape>,
then I have to cast; and it may as well have just been Cloneable.
Instead, those who implement Cloneable in specific classes should make
use of covariant return types to declare the type of the result. That
is, Object.clone() would return Object, but Shape.clone() would be
overridden to return Shape. No generics are necessary for type-safety;
you get back the same type as the receiver. No Java language feature
exists to enforce that this use of covariant returns is used, but the
only problem when it doesn't happen is that you lose some type
information.

By that logic, Cloneable should declare a:

public Cloneable clone();

(It intentionally does not throw CloneNotSupportedException, by the way.
That exception makes sense for Object, but it's clearly a programming
error if it gets thrown by a class that implements Cloneable.)

I don't see much else broken about Cloneable. There is always a danger
that someone will "forget" and not make a class clone-safe by forgetting
to make a deep copy of a mutable field... but I don't see a good way to
enforce that -- some classes such as java.util.List should *not* make
deep copies of the entire mutable portion of their object tree.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
B

Bent C Dalager

If it is mutable, it
should not be overriding equals(Object).

As a tangent to the discussion of cloning, would you relax this
requirement so that a class that is mutable can override equals _if_
it only uses immutable fields in its implementation of equals?

Or is there some more fundamental reason to this rule that I cannot
see?

Cheers
Bent D
 
C

Chris Smith

Bent C Dalager said:
As a tangent to the discussion of cloning, would you relax this
requirement so that a class that is mutable can override equals _if_
it only uses immutable fields in its implementation of equals?

Or is there some more fundamental reason to this rule that I cannot
see?

I don't see any reason to relax the requirement in those cases. In such
cases, the objects are still *not* equal. If they have a unique subset
of state that *is* equal and is useful to consider as such, then that
subset should be compared, not the objects themselves.

The problem with professing equality between mutable objects is that the
objects have unique identity and therefore aren't properly considered to
be equal to each other. If I ask whether an employee is in a list, I
really don't care whether a different employee is in the list and
happens to have the same name and salary.

There are, of course, situations where merely comparing object state is
what you want; but they tend to be very context-specific in their
meaning. In that case it's best to precisely define the meaning and
result of this comparison, and then write your own method to do it.
Overriding equals(Object) for that purpose is outright dangerous,
because the pervasive use of equals(Object) in the standard API was all
written without knowing about your redefinition of the word.

It's a losing battle, though. When event the standard API contains
value classes that are mutable (java.util.Date) and standard API classes
specify that you should override equals in unwise ways (java.util.List),
we pretty much have to consider equals to be gone as a universal fact,
and use it only within well-known type hierarchies where its meaning is
more precisely defined.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
P

Patricia Shanahan

Chris Smith wrote:

....
I couldn't disagree more strongly with this one. If a
class is immutable, it should not be exposing clone().
If it is mutable, it should not be overriding
equals(Object). Since it was already mentioned that
x.clone() != x, the implementation of equals(Object)
inherited from the Object class will return false. I
would, therefore, expect that when good design is
practiced, x.clone().equals(x) should return false.
....

How do you feel about e.g. java.util.ArrayList. Two List
objects are equal if they contain the same references in the
same order, yet ArrayList is Cloneable. Which feature would
you drop?

Patricia
 
T

Thomas G. Marshall

Chris Smith coughed up:

....[rip]...
If a class is
immutable, it should not be exposing clone().


What does immutability (the ability to be altered) have to do with
cloneability (the ability to be duplicated)?


....[rip]...
 
T

Thomas G. Marshall

Chris Smith coughed up:


....[rip]...
The problem with professing equality between mutable objects is that
the objects have unique identity and therefore aren't properly
considered to be equal to each other. If I ask whether an employee
is in a list, I really don't care whether a different employee is in
the list and happens to have the same name and salary.

If you ask if two integers are equivalent, do you care that they are
different integers that merely have the same value? No.

The notion of equality is one of current-value, or current-state.
Mutability is fine for equality.

There are, of course, situations where merely comparing object state
is what you want; but they tend to be very context-specific in their
meaning.

And I would counter that you need object state comparisons far more often
than you need true object (reference) comparisons.

....[rip]...
 
C

Chris Smith

Thomas said:
Chris Smith coughed up:

...[rip]...
If a class is
immutable, it should not be exposing clone().


What does immutability (the ability to be altered) have to do with
cloneability (the ability to be duplicated)?

Why would you clone an immutable object?

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
B

Bent C Dalager

I don't see any reason to relax the requirement in those cases. In such
cases, the objects are still *not* equal. If they have a unique subset
of state that *is* equal and is useful to consider as such, then that
subset should be compared, not the objects themselves.

Why should the client code need to be aware of this subset? The
purpose of a virtual equals() method is to encapsulate this sort of
information within the class most suited to dealing with this, namely
the class on which you invoke the equals method.
The problem with professing equality between mutable objects is that the
objects have unique identity and therefore aren't properly considered to
be equal to each other. If I ask whether an employee is in a list, I
really don't care whether a different employee is in the list and
happens to have the same name and salary.

Presumably, the employees would be compared on an employee number
rather than name and salary?
There are, of course, situations where merely comparing object state is
what you want; but they tend to be very context-specific in their
meaning. In that case it's best to precisely define the meaning and
result of this comparison, and then write your own method to do it.
Overriding equals(Object) for that purpose is outright dangerous,
because the pervasive use of equals(Object) in the standard API was all
written without knowing about your redefinition of the word.

It is not so much about redefining the word as it is about defining
what the class represents. And this is surely the task of the
programmer designing the class rather than one for the language
designers.

If you have an entity in your problem domain that has a number of
properties that define the object's identity and a number of different
properties that help define the object's current state, then surely
the equality testing should only be taking the former into account?
And if these identifying properties are immutable, then why is there a
problem?

If I am looking for a particular car, then surely I want to be
comparing on the immutable "registration number" property rather than
on the mutable "current location" property?
It's a losing battle, though. When event the standard API contains
value classes that are mutable (java.util.Date) and standard API classes
specify that you should override equals in unwise ways (java.util.List),
we pretty much have to consider equals to be gone as a universal fact,
and use it only within well-known type hierarchies where its meaning is
more precisely defined.

What exactly is it you are expecting the equals() method to represent?
I expect it to be an equality test that takes into account the type of
the object and the set of properties that best identifies it and
ignores other non-identifying properties. Is your expectation
different?

Cheers
Bent D
 
R

Ryan Stewart

Tim Tyler said:
A lot of Java's security arises not from preventing programmer error -
but from making it impossible for programmer error (or a malicious
programmer) to damage the user's system - by permitting execution to
take place in a sandbox.
That isn't true unless you're programming applets or in some other restricted
environment.
 
B

Bent C Dalager

What does immutability (the ability to be altered) have to do with
cloneability (the ability to be duplicated)?

Presumably, cloning an immutable object is just wasted effort. Since
it cannot change anyway, you may as well just retain a reference to
the original object.

I can only see this breaking down if for some reason you require two
different object instances to do separate synchronization on. But then
you might in stead want to rethink your synchronization strategy.

The only remaining problem, then, would be to find a strategy to
recognize an object as being immutable. In a case where this isn't
known a priori, I am not aware of any language mechanism or widely
adopted pattern for achieving this.

Cheers
Bent D
 
L

Lee Fesperman

Chris said:
Chris Smith coughed up:

...[rip]...
If a class is
immutable, it should not be exposing clone().


What does immutability (the ability to be altered) have to do with
cloneability (the ability to be duplicated)?

Why would you clone an immutable object?

Interesting question. It looks obvious you wouldn't. However, it seems appropriate (for
an immutable object) to implement Clonable, so certain deep copies will work. An
immutable object would just return itself. Seems strange at first, but I like it.

It does break the == contract. Nothing but hard choices ;^)
 
B

Bent C Dalager

That isn't true unless you're programming applets or in some other restricted
environment.

As far as I am aware, you can easily get this level of protection on
an arbitrary Java application by editing your security policy file. Of
course, your favourite application may not have been coded to support
that sort of thing :)

Cheers
Bent D
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,119
Latest member
IrmaNorcro
Top