Why not cloneable by default?

L

Lee Fesperman

Thomas said:
Lee Fesperman coughed up:

...[rip]...
I have been
trying to tone down my remarks on c.l.j.p lately, even apologizing
for some comments.

If you want to chat about what I think does and does not work to mitigate
online ire, we should probably do the rest by email.

Hi Thomas,

I tried to sent you private email but seem to have messed up your instructions for
fixup. Can you send me an email so I can respond? I always use my exact email address. I
just live with the spam ;^)
 
C

Chris Smith

Bent C Dalager said:
I take it that you mean to say that not everything that is a Java
object is an OO object? If so, then I might not have a problem with
that. I am referring to the use of the equals() method on Java objects
and if my use turns these Java objects conceptually into OO values
that is not a problem. In this case, our disagreement is based on a
misunderstanding of terminology.

Absolutely. I would almost say that it's undeniably true, and
fundamental to the purpose of the equals(Object) method, that Java
objects can fail to be OO objects in the practical sense. That is,
while they always *have* identity, it is frequently the case that their
identity is practically meaningless, and should be ignored. There even
exist "lint" tools to ensure that you don't depend on the identity of
String objects, for example. What you have, then, is no longer
functioning as an object.
In Java, [identity] is provided by the == operator. I still don't see
why that should have consequences for the use of the equals() method.

Then we're back to the main point of this subthread. The point of
equals(Object), as best I can figure by its most common usage in the
oldest and most common parts of the Java API, is exactly this: to
provide a concept of several reference variables having the same logical
value, where the meaning of "same" can be controlled by the object
itself. Use of the '==' operator to compare references means depending
on specific properties of the resulting object; namely, that whether two
references are the same depends on whether they point to the same object
or not. That's true for proper objects, but not for value objects.

The practical consequence of this is that objects which redefine equals
(this is implicit in the process of choosing a generally useful concept
of equality, as you've mentioned before) work very poorly and are
fragile with existing API functions that depend on the definition of
equals. See, for example, any discussion on the pitfalls of using
mutable objects as keys in a HashMap, or of adding them to any kind of
set. In truth, of course, mutable objects are fine *except* when they
override equals to perform some kind of generally useful comparison.

The bigger problem, though, is that without knowing the implementation,
you can't decide whether it's safe or not to use such an object, and
people assume it is. As an interesting aside, Lee just demonstrated
this concept by saying that List.contains seems safe to use with mutable
objects. Of course it's not; only specific implementations such as
ArrayList or LinkedList could be considered safe. In the general case,
*nothing* that uses objects in general is safe when given such an
object, unless it specifically documents that it is.

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

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

Stefan Ram

Chris Smith said:
while they always *have* identity, it is frequently the case
that their identity is practically meaningless, and should be
ignored. There even exist "lint" tools to ensure that you
don't depend on the identity of String objects, for example.
What you have, then, is no longer functioning as an object.

That is to say, as an /value object/, i.e., an object
whose identity is intended to be defined by its /value/.

Such value objects are one kind of objects, albeit not
the only type.
 
B

Bent C Dalager

Absolutely. I would almost say that it's undeniably true, and
fundamental to the purpose of the equals(Object) method, that Java
objects can fail to be OO objects in the practical sense. That is,
while they always *have* identity, it is frequently the case that their
identity is practically meaningless, and should be ignored. There even
exist "lint" tools to ensure that you don't depend on the identity of
String objects, for example. What you have, then, is no longer
functioning as an object.

Ok. I can accept this terminology. It will probably save us some grief
:)
The practical consequence of this is that objects which redefine equals
(this is implicit in the process of choosing a generally useful concept
of equality, as you've mentioned before) work very poorly and are
fragile with existing API functions that depend on the definition of
equals. See, for example, any discussion on the pitfalls of using
mutable objects as keys in a HashMap, or of adding them to any kind of
set. In truth, of course, mutable objects are fine *except* when they
override equals to perform some kind of generally useful comparison.

I feel that this is approximately where I originally got into the
debate. I agree that using a mutable object's equals() can be risky
and should only be attempted after understanding how that class
works. My original point, however, if I remember correctly, was that
if all of the fields that are actually used by equals() are immutable,
then this risk goes away.

There may still be a problem with not wanting to have mutable objects
at all, and/or there may be a problem with not wanting to call such an
object an "object" but rather a "value" since its equals() is not the
same as its ==. I don't particularly challenge either of these two
objections. My focus was on the safeness of use and so long as the
result of equals() always returns the same throughout an object's life
and regardless of the mutations performed on it during that life, maps
and sets and sorted lists and what have you all behave as advertised.
The bigger problem, though, is that without knowing the implementation,
you can't decide whether it's safe or not to use such an object, and
people assume it is. As an interesting aside, Lee just demonstrated
this concept by saying that List.contains seems safe to use with mutable
objects. Of course it's not; only specific implementations such as
ArrayList or LinkedList could be considered safe. In the general case,
*nothing* that uses objects in general is safe when given such an
object, unless it specifically documents that it is.

This is true. This sort of thing should be thoroughly documented in
the apidoc and any object that overrides equals() without satisfactory
documentation should never be trusted to behave the way you need it
to.

Cheers
Bent D
 
T

Thomas G. Marshall

Chris Smith coughed up:
Bent C Dalager said:
I take it that you mean to say that not everything that is a Java
object is an OO object? If so, then I might not have a problem with
that. I am referring to the use of the equals() method on Java
objects and if my use turns these Java objects conceptually into OO
values that is not a problem. In this case, our disagreement is
based on a misunderstanding of terminology.

Absolutely. I would almost say that it's undeniably true, and
fundamental to the purpose of the equals(Object) method, that Java
objects can fail to be OO objects in the practical sense. That is,
while they always *have* identity, it is frequently the case that
their identity is practically meaningless, and should be ignored.
There even exist "lint" tools to ensure that you don't depend on the
identity of String objects, for example. What you have, then, is no
longer functioning as an object.
In Java, [identity] is provided by the == operator. I still don't see
why that should have consequences for the use of the equals() method.

Then we're back to the main point of this subthread. The point of
equals(Object), as best I can figure by its most common usage in the
oldest and most common parts of the Java API, is exactly this: to
provide a concept of several reference variables having the same
logical value, where the meaning of "same" can be controlled by the
object itself. Use of the '==' operator to compare references means
depending on specific properties of the resulting object; namely,
that whether two references are the same depends on whether they
point to the same object or not. That's true for proper objects, but
not for value objects.

The practical consequence of this is that objects which redefine
equals (this is implicit in the process of choosing a generally
useful concept of equality, as you've mentioned before) work very
poorly and are fragile with existing API functions that depend on the
definition of equals. See, for example, any discussion on the
pitfalls of using mutable objects as keys in a HashMap, or of adding
them to any kind of set. In truth, of course, mutable objects are
fine *except* when they override equals to perform some kind of
generally useful comparison.

The bigger problem, though, is that without knowing the
implementation, you can't decide whether it's safe or not to use such
an object, and people assume it is.

Yes. Value comparison is and always has been dependent upon documentation.
Nothing wrong with that.

As an interesting aside, Lee
just demonstrated this concept by saying that List.contains seems
safe to use with mutable objects. Of course it's not; only specific
implementations such as ArrayList or LinkedList could be considered
safe. In the general case, *nothing* that uses objects in general is
safe when given such an object, unless it specifically documents that
it is.

Ok. After reading everything you've had to say on the matter, which has
been very consistent throughout yet somehow exhausting to read anyway, I
have to disagree with most of this.

When you compare the "value" of a mutable object using equals(), you are not
taking a "risk" per se. You are legitimately asking the object for its own
ruling on the subject /at the moment/. There is nothing inconsistent with
this and OO principals and/or "good design". All concerns I have as to the
acceptability of using an object's own value judgments go by the wayside as
soon as I accept that the judgment is good at the moment (as we all do with
mutable objects), or if you prefer: up until the next mutation of either
comparing object.

In other words, of course, and that's no big deal. I doubt most language
designers would worry about this either, but not being one, I honestly
cannot say with authority.
 
C

Chris Smith

Stefan Ram said:
That is to say, as an /value object/, i.e., an object
whose identity is intended to be defined by its /value/.

Such value objects are one kind of objects, albeit not
the only type.

They are certainly objects in Java. They are not objects in OO theory.
As soon as you say that an object's identity can be "defined by its
value", you have thrown away the concept of identity. At that point,
you no longer have a proper object. This isn't the main point of the
conversation, but it is fundamental to OO theory.

That's not to say there something wrong with defining such a thing as an
object minus identity. In fact, it often makes a lot of sense.

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

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

Chris Smith

Thomas said:
Yes. Value comparison is and always has been dependent upon documentation.
Nothing wrong with that.

Indeed, there is something wrong with that. The problem is precisely
that an equals(Object) method defined in class Object needs to be
applicable in the context of an Object. I know you've asked me the
question in the past of whether the issue could be satisfied by removing
equals(Object) from the Object class. While that at least acknowledges
the problem with implementing context-specific meanings of equals, I
think that it makes a grave error in solving that problem.
Specifically, it ignores that there is a universal definition of
equality in the context of java.lang.Object.

It actually makes perfect sense to compare objects for their
equivalence, regardless of whether they are of wildly differing classes
and bear no relationship whatsoever in form or purpose. There are, in
fact, any number of classes that use this form of comparison; including,
among others, practically all implementations of any types defined in
the Collections API, excepting SortedSet and SortedMap. It is,
therefore, empirically false that defining equality of objects must be
specific to the type of object.

These uses of Object.equals work fine while that implied contract is
upheld, that the objects are equal as an inherent characteristic -- not
that their contents are equal. The word contents, incidentally, never
occurs in the specification for Object.equals, nor do any of its
synonyms; the phrases chosen by the spec are that the objects themselves
are "equal to" and "the same as" each other. The only time that such
code, written to use Object.equals without knowing the Object type,
breaks is when objects are declared equal to each other when they are
not equal in and of themselves. And when that occurs and situations are
such that the inequality becomes apparent (i.e., you're not exercising
the self-discipline to pretend that the object is immutable when it is
not), then such code practically ALWAYS breaks.
When you compare the "value" of a mutable object using equals() you are not
taking a "risk" per se. You are legitimately asking the object for its own
ruling on the subject /at the moment/.

Note the two unspoken assumptions here:

1. That you know something about what kind of objects you are comparing,
making this statement at least only tangentially relevant to the
definition of a method defined in the Object class.

2. That it's "you" who are asking, and not some of the body of
established code already written to ask this question, practically all
of which makes certain assumptions about the contract provided by equals
-- an assumed contract which is unfortunately, and with substantial
cost, not stated in Object itself or in common books on Java and ignored
by a few frequently used classes.

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

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

Thomas G. Marshall

Chris Smith coughed up:
Indeed, there is something wrong with that. The problem is precisely
that an equals(Object) method defined in class Object needs to be
applicable in the context of an Object. I know you've asked me the
question in the past of whether the issue could be satisfied by
removing equals(Object) from the Object class. While that at least
acknowledges the problem with implementing context-specific meanings
of equals,

No, it doesn't, not exactly. What I was pointing out was that having
equals() mean potentially *both* identity or value comparison is what is of
dubious value, and I'd understand if it bothered you. What the java
designers were attempting was (IMO) 1. to blend the notions in that either
could take place, and 2. make sure that even the following:

public boolean arbitraryCheck(Object obj)
{
[...code...]
return obj.equals(somethingUnknownAtCompileTime);
}

would possibly have value checking if at all possible (very subtle
distinction). There's nothing here of course that you don't know.

Maybe I'd prefer the following?

Object.contentsEqual(boolean): does a /kind/ of JNI bit for bit
compare.
MyThingy.contentsEqual(boolean): equality check for 4 ints and a
string or two.

Or perhaps nothing in Object at all. But I /would/ feel better with a
minimal compile-time suggestion as to what method signature to use, so maybe
a bit for bit compare of some kind would have made a sensible architecture,
back when they were first designing the language. Or maybe:

public boolean equals(Object obj)
{
// no throws clause needed
throw UnimplementedValueComparisonRuntimeException();
}

I think that it makes a grave error in solving that
problem. Specifically, it ignores that there is a universal
definition of equality in the context of java.lang.Object.

It actually makes perfect sense to compare objects for their
equivalence, regardless of whether they are of wildly differing
classes and bear no relationship whatsoever in form or purpose.
There are, in fact, any number of classes that use this form of
comparison; including, among others, practically all implementations
of any types defined in the Collections API, excepting SortedSet and
SortedMap. It is, therefore, empirically false that defining
equality of objects must be specific to the type of object.

These uses of Object.equals work fine while that implied contract is
upheld, that the objects are equal as an inherent characteristic --
not that their contents are equal. The word contents, incidentally,
never occurs in the specification for Object.equals, nor do any of its
synonyms; the phrases chosen by the spec are that the objects
themselves are "equal to" and "the same as" each other.

There is no notion of "equal to" or "the same as" that is written in one of
the stone tablets. Perhaps the third one dropped by Mel Brooks.

"Equality" in all things, requires a definition. It is often implied,
because as a laguage creature, we use terms that don't always need continual
explanation.

"Jim's salary is equal to Steve's", would work in the context of two people
talking at a bar comparing $100,000 to $103,000.

"These two cars are the same", usually if they are the same model, the same
configuration, the same cost, but not usually the same color. Color just
wasn't part of the definition of equals().

Similarly for integers in C

(i == j)

is not

(&i == &j)

Because that is not what equals is defined as.

So context sensitive comparison is everywhere, certainly in the real world,
and in every object we create.

The only
time that such code, written to use Object.equals without knowing the
Object type, breaks is when objects are declared equal to each other
when they are not equal in and of themselves. And when that occurs
and situations are such that the inequality becomes apparent (i.e.,
you're not exercising the self-discipline

Also called "making a mistake".

to pretend that the object
is immutable when it is not), then such code practically ALWAYS
breaks.

Mutating a purported immutable object is a bug. I don't see stopping that
by squashing value comparisons that are the same method name as
Object.equals().

Note the two unspoken assumptions here:

1. That you know something about what kind of objects you are
comparing, making this statement at least only tangentially relevant
to the definition of a method defined in the Object class.

2. That it's "you" who are asking, and not some of the body of
established code already written to ask this question, practically all
of which makes certain assumptions about the contract provided by
equals

There is no contract provided by equals that lives in a vacuum outside of
the documentation.

In other words, as you've acknowledged, thingy.equals(otherThingy) has no
inherent meaning.

And I see nothing wrong with that. The equals() facility is there precisely
to allow context driven (read "documentation driven" if you prefer)
behavior. That this happens is a beautiful feature of it.
 
T

Thomas G. Marshall

Chris Smith coughed up:
They are certainly objects in Java. They are not objects in OO
theory. As soon as you say that an object's identity can be "defined
by its value",

But spit.equals(splat) does not try to establish identity. Identity is "who
it is". In Java, a reference identity is all you get. But saying that
there is a value comparison does not imply, nor do I ever see anyone else
state, that it is establishing identity.

If you did, you'd be asking for a serious bug down the road if it weren't
strictly obeyed that all values (identities in that case) were unique.
 
L

Lee Fesperman

Chris said:
The bigger problem, though, is that without knowing the implementation,
you can't decide whether it's safe or not to use such an object, and
people assume it is. As an interesting aside, Lee just demonstrated
this concept by saying that List.contains seems safe to use with mutable
objects. Of course it's not; only specific implementations such as
ArrayList or LinkedList could be considered safe. In the general case,
*nothing* that uses objects in general is safe when given such an
object, unless it specifically documents that it is.

Since other of our sub-threads including the one you're quoting seem to be dead, I'll
jump into this one. I don't quite follow what you are saying about List and didn't find
support in the javadocs I looked at. Perhaps, you could explain more.

Failing that, perhaps you (and others) could comment on and suggest changes to the
boilerplate used for the mutable wrappers I mentioned elsewhere:

"Note: When the wrapped value changes, the action of hashCode() and equals() also
changes. This may be a consideration when using mutable wrappers where hashCode() needs
to be immutable, such as, keys in java.util.Map and java.util.Set."
 
C

Chris Uppal

(I've been following this sub-thread, but I can't find a suitable place to jump
aboard, so I'll reply here)

Chris said:
[snipped]

I've been struggling to understand your position in this. The following is (in
part) an attempt to recover the argument that must underlie it. I'm reasonably
happy that it makes sense, but am less confident that it adequately captures
your argument -- not least because I'm unable to recover a theoretical
justification for an emphasis on immutability...

Starting in the abstract (not Java specific, and not assuming OO, although I
will use the word "objects" -- for lack of anything better).

We want to be able to express the notion of The One True Equality: that two
"objects" are truly equal if they cannot be distinguished in any way, and so
that "they" are fully interchangeable (since if we could distinguish between
them by using one of them in context, rather than the other, then they wouldn't
be interchangeable, and we'd have a test that could distinguish between them).
Call that "unconditional equivalence" or just "equivalence" (since the proper
application of the word "equality" is part of what's under discussion in this
thread).

Now lets introduce a little OO into the mix. That means that we get the idea
of object identity to play with. I don't yet want to make any assumptions
about the proper relationship between unconditional equivalence and object
identity except to note that obviously an object is "equivalent" to itself (we
can say that now that we've introduced objects and so can use concepts like
"itself").

Now lets introduce a new concept. Unfortunately I can't think of a good name
for it that doesn't bring conceptual baggage with it (and precisely the
conceptual baggage that I'm attempting to understand), so I'll use a bland
word. Some objects are (or play a role that is) "special", in that no other
object is allowed to play that role unless it is unconditionally equivalent to
the first. An example of this is any instance of java.lang.Class, say
java.lang.String.class. That object is "special" in that any "other" object
that purports to play that role /must/ be indistinguishable from it by any
possible test (that's expressible in Java).

Note that being "special" does not in any way imply being immutable. Also note
that I have not yet made any connection between what it is to be "special" and
the concept of object identity.

[As an almost completely irrelevant aside -- I'm not sure that "immutable" is
even a first rank OO concept, at least not in the hard-line version of OO that
I preach. "state" (and hence [im]mutability) is merely part of the
implementation of an object, and hence not to be accorded importance comparable
with that of identity and behaviour. "state" is just one of the ways that we
achieve behaviour that differs polymorphically between objects and changes
across time.]

Now for another concept that I don't have a theory-neutral word for. Call an
object "content defined" if it has the property that once you know a certain
amount about it, then you know /everything/. Intuitively you could say that if
two "objects" have the same state then they are unconditionally equivalent.
(But that is a bad approximation -- see the above caveat about "state" -- a
better formulation would probably talk about there being a fixed (in advance)
algorithm that compared finitely many behaviours of the "two" objects, and
which if it could not find a difference, then that would imply that no other
behaviour could ever exhibit any difference.) One example of this might be a
document (a PDF or similar). Once you know how many bytes it has (or
characters if your prefer) and which bytes they are, then (in many contexts)
you know /everything/ about that document -- there is simply no room for two
documents to have the same contents but nevertheless be "different". (And if
the documents are stored in a filesystem in such a way that the same document
may appear as (i.e. "in") more than one file, then that may well lead to
semantic errors since the semantics of "files" don't correctly represent those
of "documents".)

Note that being "content defined" does not imply immutability. (Which is one
reason why I didn't call the property having "value semantics" since that -- to
me -- does imply immutability. The other reason is that "value semantics" may
be taken as implying a definite stance w.r.t. object identity -- which I'm
still trying to avoid). OTOH, to be "content defined" /is/ to be
special" -- its the limit case where the external part of the role of the
object (that contributes to "special"-ness) has shrunk to zero.

[BTW. I'm sorry that this is so slow and long-winded. I'm inching through it
because I think this area is something of a conceptual minefield, and I'd like
to retain the use of /both/ my legs ;-)]

That -- at last -- is the end of the groundwork. Now we can consider how to
implement these concepts, and specifically how to implement them in the world
of OO.

AFAIK, there are just three ways that we can implement "special"-ness. None is
perfect, and the three techniques have different trade-offs between them:

a) The VM itself can understand this concept and provide the correct semantics
directly.

b) We can implement "special" by /imposing/ uniqueness -- using object identity
to implement unconditional equivalence.

c) We can implement "special" by providing a customised version of the test for
equivalence, one that understands that two distinct objects can nevertheless be
considered to be equivalent. In this case you could say that we are
representing the abstract "special" object by an equivalence class of actual
objects.

Option (a) requires VM support, and is even then only applicable to certain
cases. Option (b) may require more housekeeping effort than its worth (e.g.
you might have to maintain some sort of weak collection of objects, and check
that whenever a new object is created to ensure that you don't create
illegitimate duplicates). Option (c) suffers from the problem that object
identity /isn't/ hidden. Not only does it "leak out" through == comparison (in
Java) but also through (at least) identity hashing, weak collections,
finalisation, synchronisation, and the operations wait() and
notify()/notifyAll().

I want to emphasise that (at least as I see it) these are /implementation/
techniques. Different ways of attempting to map our abstract notion of what it
is to be "special" onto the real behaviour of concrete objects.

[As yet another aside, and in an attempt to bolster the above assertion, I'll
mention that Smalltalk makes use of all three techniques to implement its
equivalents of Java's primitive types. The number 122 is undoubtedly
"special" -- you don't want there to be two distinguishable 122s in a system --
but that can be implemented in various ways. The Smalltalk implementation I
work with uses (a) for integers that can be expressed in 31 or less bits, and
uses (c) for integers outside that range and for floating point numbers. It
uses (b) for characters. This is an area where ST implementations vary, for
instance some use (a) for some floats and/or characters.]

Of course, the /big/ problem with option (c) is that it is incompatible with
object mutability. You might be able to live with the leakages I mentioned
above (if you are lucky and/or disciplined), but if you allow the objects to
change state in any detectable way, then you don't stand a hope of maintaining
the correct semantics for unconditional equivalence.

One thing I want to emphasise a little, is that although (c) is incompatible
with mutability, (b) is an implementation of the same concept that is not
incompatible. I.e. being "content defined" does in itself imply immutability
(once some niggles about mutating one object to be "the same" as another are
ironed out). And of course it is perfectly possible to be "special" and
mutable.

Chris Smith mentioned the non-OO, or at best pseudo-OO nature of pure values.
If you have such concepts that you wish to map onto an OO implementation then
"they" are certainly "content defined" (in my sense) and hence "special". In
such cases (c) is a decent implementation choice -- you are unlikely to want to
change the state of such objects (what would that even mean ?) and the way that
(c) subverts the object-nature of the thing represented would not be a
significant problem when you aren't trying to represent some "thing" in the
first place. (Actually (a) might be even better, but that's not an available
option in Java.)

But (b) is still a valid alternative design, even for implementing value
semantics (it would amount to do doing some sort of interning of the "values").
And for the wider case where all that's required is "content defined" it may
even be a better choice (at least if the content is also immutable). In the
wider still case where we're trying to implement equivalence for "special"
objects, technique (b) may be the only possible option.

Digressing slightly: how should "equivalence" be spelled in Java ? It doesn't
seem to me that Java serves us very well here. Ideally, the One True Equality
ought to be spelled '==' (or '=' but that's out for obvious reasons). Any
other spelling is likely to lead to fragile code -- it's simply not reasonable
to expect programmers to remember to call a method with a name like
equivalent() or equals() every time. So, ideally we would have some sort of =
operator that would expand out to a VM primitive when technique (a) was in use
(and for primitive types -- which are a sort of special-case of (a) anyway).
It would expand out to an identity comparison when (b) was used:
return (this issameobejctas that);
and would expand out to a comparison of the contents of "content defined"
objects (with maybe an identity comparison first as an optimisation) if they
were implemented using (c).

Of course, Java doesn't allow that. So we are stuck with trying to shoe-horn
our One True Equality into one or the other of:
explicit identity comparison
a call to equals()
(we could also define our own unconditionallyEquivalentTo() method, but I don't
think that offers much in the way of practical advantages over plain old
equals()). It's tempting to say that all comparison must be done via the
equals() method, since that can (internally) use a field-by-field comparison or
an identity comparison as appropriate to the implementation (b) or (c). But --
even without the problems caused by nulls -- I don't think that's very
practical. It seems (on the whole, and as the lesser of two evils) better to
lift the skirt of the implementation a little and allow programmers to "know"
which operation they are supposed to use to compare which objects.

BTW, I've restricted this discussion to the case where our notion of
equivalence has some non-trivial structure w.r.t the notion of object identity.
In the most general case the only object that is unconditionally equivalent to
(cannot possibly be distinguished from) some object, is that object itself. So,
in general, unconditional equivalence degenerates (uncontentiously) to object
identity. The interesting cases are the ones where the semantics allow/require
a less degenerate solution.

Anyway, at the end of all that, I can (to a degree) accept that:
value semantics -> non-mutable -> override (and use) equals()
but I think it's also important to note that:
a) that's not the only possible implementation of value semantics.
b) value semantics is not the only legitimate application of that
implementation technique.

=========

Attempting to relate this stuff to the ostensible topic of the thread ;-) I
think that any object that is "special" /can/ legitimately implement clone() as
"return this;". Some implementations (b) /must/ implement it like that, but an
implementation that uses (c) may also choose to do so for performance reasons
(it's a legitimate optimisation since "special" objects implemented using (c)
are necessarily immutable).

=========

Now, to return to a couple of subjects that I skimped in the foregoing. I
rather assumed two things: one is that there is a need for a One True Equals
test at all, then second is that the method named equals() is intended to
correspond to that condition.

Let's define another comparison: "pretty similar" -- two objects will be
"pretty similar" iff (according to the class designer's intuition) the two
objects would be considered to be "the same" in many contexts. Being "the
same" is obviously application and context dependent; being "pretty similar" is
a specific, hardwired, choice of some context(s) as most significant or most
useful.

Obviously there's a progression of comparisons:
Object identity => unqualified equivalence => pretty similar(ity)
(where '=>' is logical implication). Similarly the natural /default/
implementation of "pretty similar" is "unqualified equivalence" and that of
"unqualified equivalence" is object identity.

Incidentally, given those natural defaults, it is impossible to tell from the
actual implementations of equals() in the standard libraries whether they are
intended to implement equivalence or mere similarity. Is Object.equals() an
implementation of "pretty similar" (implemented as a fallback to equivalence)
or is it an implementation of "unqualified equivalence" that is mistakenly
overridden by implementations that look more like "pretty similar" ?

I would like to claim that equals() should not in fact be viewed as an
implementation of "unqualified equivalence", but that it is easier to
understand and use it if it is seen as meaning "pretty similar" (noting, of
course, that the two notions are often identical).

For instance, Chris Smith mentions the example of Date. If I'm interpreting
him correctly (and not reading too much into a throwaway remark) he sees Date
as an attempt to implement value semantics using the technique I've labelled
(c) (overriding equals() to conceal the difference between distinct objects
that represent the same date). Seen from that POV, Date is broken because
although it has a correct implementation of "unqualified equivalence" as
equals(), the objects are mutable which means that implementation technique (c)
is not a valid way to represent "content defined" values. I don't dispute that
interpretation, but I also don't think its the best interpretation to make. If
instead you see Dates as "just plain objects" (not value objects, not "content
defined") then "unqualified equivalence" for Dates degenerates to object
identity (so there's no problem expressing that if/when we wish), and the
natural (or at least /a/ natural) interpretation of "pretty similar" is exposed
by the equals() method. Seen that way, Dates are full objects (not
pseudo-objects i.e. their object nature is not merely an artefact of the way
Java works) -- which is certainly how /I/ see Dates -- there's nothing
particularly wrong with them being mutable (it may be a poor design, but its
not a semantic error), and nothing is broken about the equals() method, indeed
it does just what you'd expect.

OTOH, seeing things like this doesn't only lead to a new tolerance for the
standard library -- it does suggest criticisms too. If equals() is seen as
being the One True Equals, then it is at least understandable (if not
forgivable) that the standard collections do not always provide a way to use an
alternate comparison criterion. If you see it my way (which I think is "pretty
similar" to Thomas's position) then it becomes harder to understand why nobody
at Sun (or Apache) has apparently seen the need for a HashSet (etc) with a
pluggable "equality" criterion.

[After all those words I feel I should end up with some sort of summary or
maybe draw a moral -- but I can't think of one...]

-- chris
 
T

Thomas G. Marshall

Chris Smith coughed up:
I don't think we're understanding each other yet. In fact, I believe
that equals(Object) has a critically important role to play -- for
pseudo-objects. That is, Java provides objects as the *only* way of
creating user-defined data types. There are, however, plenty of non-
primitive types (e.g., Date) that do not need object semantics.

I have often in the past argued that, in fact, the '==' operator in
Java when applied to reference types should merely call Object.equals
unless one or both operands are null. Although this is impractical
in a modern Java where the purpose of equals(Object) is vague and
confused, it's still not a bad theoretical idea. IMO, it is a
mistake to ever compare references, unless you KNOW that
.equals(Object) will give back the same answer and you are only doing
the reference comparison for performance.

So I think I'm quite far from arguing that equals(Object) should not
exist; I think that == should not exist, except as a shorthand for the
former, with its current behavior only available when inherited from
java.lang.Object.

Mutability comes about because it conflicts with certain important
characteristics of objects that are "equal" to each other; namely,
that they can't become unequal later on. I could put it differently,
though. Basically, equals(Object) should only be overridden in
classes that desire value semantics instead of object semantics.
Since such classes must be immutable anyway, it naturally follows
that mutable classes should not override equals(Object)


I apologize if I'm overstepping here, but there are great minds in c.o that
might want to analyze this (and agree or not).
 
C

Chris Smith

Lee Fesperman said:
Since other of our sub-threads including the one you're quoting seem to be dead, I'll
jump into this one. I don't quite follow what you are saying about List and didn't find
support in the javadocs I looked at. Perhaps, you could explain more.

I simply mean that List doesn't really say anything about the use of
equals(Object) between its members. As a result, you can't really know
that it doesn't use equals(Object) (or the related hashCode() for that
matter) to perform some task. For example, I might provide a List which
is specifically designed for fast implementation of contains and indexOf
operations, and therefore actually contains a hashing data structure
internally. Changing the elements of that List in a way that modifies
equality (or hashing) could break things.
Failing that, perhaps you (and others) could comment on and suggest changes to the
boilerplate used for the mutable wrappers I mentioned elsewhere:

Nope. Looks fine to me.

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

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

John C. Bollinger

Chris said:
[As an almost completely irrelevant aside -- I'm not sure that "immutable" is
even a first rank OO concept, at least not in the hard-line version of OO that
I preach. "state" (and hence [im]mutability) is merely part of the
implementation of an object, and hence not to be accorded importance comparable
with that of identity and behaviour. "state" is just one of the ways that we
achieve behaviour that differs polymorphically between objects and changes
across time.]

What if one could define "immutability" in terms of object behavior?
For instance, "An object is immutable if it enforces the rule that every
behavior it expresses has identical observable results every time it is
invoked with the same inputs." Does that not effectively capture the
concept?
 
S

Stefan Ram

Chris Smith said:
They are not objects in OO theory.

Is there a theory widely accepted as /the OO theory/?

What would be a textbook or monograph describing this theory?
As soon as you say that an object's identity can be "defined by
its value", you have thrown away the concept of identity.

To me it would be /another/ concept of identity.

I can agree to your suggestion not to call these objects.

It is not easy, nowadays, to find unused words, but I
will now use "entity".

Definition 0:

An integral number in mathematics has an identity, which is
completely defined by its value. Such an entity I call a
"value entity".

Definition 1:

A physical system, like a bit storage, has an identity,
which is /not/ defined by its value, but by an additional
primary key. Such a primary key might be given explicitly,
as in the theory of relational databases, or it might be
given implicitly (in a sense, in Java, the reference to an
object is like its primary key). Such an entity I call a
"storage entity".

Observation:

One sometimes wants to use objects to model value entities,
and one sometimes wants to use objects to model storage
entities.

Example:

In Java, java.lang.String or java.lang.Integer seems to be
intended to model value entities, because their instances
can not be modified. This model is not perfect, because, two
instances with the same value can be found to differ by
their reference. A java.lang.StringBuilder instance is
intended to model a storage entity.
 
L

Lee Fesperman

Chris said:
I simply mean that List doesn't really say anything about the use of
equals(Object) between its members. As a result, you can't really know
that it doesn't use equals(Object) (or the related hashCode() for that
matter) to perform some task. For example, I might provide a List which
is specifically designed for fast implementation of contains and indexOf
operations, and therefore actually contains a hashing data structure
internally. Changing the elements of that List in a way that modifies
equality (or hashing) could break things.

After thinking about it some more, I realized you were alluding to something like the
above. As you say, List makes no comment about equals(Object) between its members nor
about hashCode() at all, so there is little a specific overriding of both can do except
explain any special aspects of its implementation.
Nope. Looks fine to me.

Thanks. I posted it because of your prior comments about List. Actually, this software
is not yet released; it's still in Beta. It seemed a good time for any refinements to
the javadocs.

Mutability in these classes is intended for a very specific purpose: to provide
input/output semantics for a stored procedure implementation. Technically, the value
changes occur at the server. The client sends one object and receives back a different,
changed object. The client wouldn't actually make any changes in most cases, thus they
could treat the objects as 'sufficiently' immutable to store them in Collection objects.
Note: they can also retrieve the immutable wrapped object.

In the past, we left it to the users to build their own mutable wrappers to get
input/output semantics. When we implemented an ODBC driver, we decided to standardize
the wrappers for basic types, so input/output semantics were available to ODBC clients.
We avoided doing full serialization in the ODBC driver by using simple templates to
recognize supported objects (mutable wrappers) and extract their wrapped values on input
and by using the templates to wrap values on output, to the server. This works very
nicely.
 
C

Chris Smith

Chris Uppal said:
I've been struggling to understand your position in this. The following is (in
part) an attempt to recover the argument that must underlie it. I'm reasonably
happy that it makes sense, but am less confident that it adequately captures
your argument -- not least because I'm unable to recover a theoretical
justification for an emphasis on immutability...

I'm not sure of this either, probably because I don't understand exactly
what you mean in certain cases.
We want to be able to express the notion of The One True Equality: that two
"objects" are truly equal if they cannot be distinguished in any way, and so
that "they" are fully interchangeable (since if we could distinguish between
them by using one of them in context, rather than the other, then they wouldn't
be interchangeable, and we'd have a test that could distinguish between them).
Call that "unconditional equivalence" or just "equivalence" (since the proper
application of the word "equality" is part of what's under discussion in this
thread).

That sounds like what I'm looking for, and what I think equals(Object)
does most of the time, except in places where that purpose was lost.
I'd add a caveat, though.

While "One True Equality" is an amusing and flashy phrase, I'd be more
likely to express it as "equality as a java.lang.Object". In other
words, we're looking for a useful concept of equality that applies in
the context of knowing only that you are working with a
java.lang.Object. As others have pointed out, there are other very
useful equalities that are no less deserving of the term; but
Object.equals simply doesn't apply to them because it's a method in
Object.
Now lets introduce a new concept. Unfortunately I can't think of a good name
for it that doesn't bring conceptual baggage with it (and precisely the
conceptual baggage that I'm attempting to understand), so I'll use a bland
word. Some objects are (or play a role that is) "special", in that no other
object is allowed to play that role unless it is unconditionally equivalent to
the first.

Okay. I'm not entirely sure I understand what you mean by "allowed to
play that role", but I'll plunge ahead as if I do.
Note that being "special" does not in any way imply being immutable.

Depending on your definition of "immutable", I'd argue that being
"special" certainly does imply immutability. Lets define mutability as
the property that, given an object which exists at time T1, its behavior
is defined at time T2 independently of any events that occur between T1
and T2. I have defined immutability in a way that has no dependence on
state, in order to satisfy your issues elsewhere.

Now, if one of two objects A and B is mutable, then I can tell you a
very definite means of distinguishing A and B, which are otherwise
equivalent. Namely:

1. Make some modification to object A that will affect future behavior.
2. Compare that specific behavior of objects A and B.

And voila, I've distinguished them. By your earlier definition of
"unconditional equivalence", which is that there is no test to
distinguish them, A and B are not unconditionally equivalent.

It occurs to me that you might be using some other definition of
"immutable", specifically related to the question of whether any of the
object's fields may change. In that case, I would agree that
immutability is irrelevant. For example, two self-modifying lookup
trees, which allow only lookups and not insertions or deletions, might
be unconditionally equivalent; but they would still be free to re-
arrange the structure of their trees, for example, to make a second
lookup of the same object faster. Then I'd invent some other condition
(call it "changeability", perhaps) and define it as I've proposed for
immutability above, and insist on that instead.
AFAIK, there are just three ways that we can implement "special"-ness. None is
perfect, and the three techniques have different trade-offs between them:

a) The VM itself can understand this concept and provide the correct semantics
directly.

b) We can implement "special" by /imposing/ uniqueness -- using object identity
to implement unconditional equivalence.

c) We can implement "special" by providing a customised version of the test for
equivalence, one that understands that two distinct objects can nevertheless be
considered to be equivalent. In this case you could say that we are
representing the abstract "special" object by an equivalence class of actual
objects.

Clearly, a) is not an option, however, since such a thing is not
provided by the virtual machine. That leaves us with b) and c).

I like your wording for c) by the way; it very well captures the nature
of the issue; namely, that any number of Java-language objects may
exist, but that there is still only one conceptual entity in existence.
I want to emphasise that (at least as I see it) these are /implementation/
techniques. Different ways of attempting to map our abstract notion of what it
is to be "special" onto the real behaviour of concrete objects.

In a sense, they are implementation techniques. However, Java does not
provide enough of an abstraction layer to ensure that they really are
hidden within the implementation. Therefore, they are implementations
of the concept, but not "implementation" in the sense of "something a
client need not care about"... at least in Java. I am assuming that's
what you meant.

Incidentally, that latter fact is almost entirely due to Java having
provided an == operator that guarantees a test for reference equality
and a System.identityHashCode() that can be called on arbitrary objects;
instead of both being exclusively inherited as an implementation from
java.lang.Object. I am becoming increasingly convinced, over time, that
this was a mistake on James Gosling's part, though an understandable one
at the time it was made.
But (b) is still a valid alternative design, even for implementing value
semantics (it would amount to do doing some sort of interning of the "values").
And for the wider case where all that's required is "content defined" it may
even be a better choice (at least if the content is also immutable). In the
wider still case where we're trying to implement equivalence for "special"
objects, technique (b) may be the only possible option.

This is, indeed, true. However, it doesn't conflict with requiring
immutable types when overriding equals in Java. Using your technique
b), I don't see why Object.equals would be overridden.
Anyway, at the end of all that, I can (to a degree) accept that:
value semantics -> non-mutable -> override (and use) equals()
but I think it's also important to note that:
a) that's not the only possible implementation of value semantics.
b) value semantics is not the only legitimate application of that
implementation technique.

I don't see where you're getting point b here. Can you explain?
For instance, Chris Smith mentions the example of Date. If I'm interpreting
him correctly (and not reading too much into a throwaway remark) he sees Date
as an attempt to implement value semantics using the technique I've labelled
(c) (overriding equals() to conceal the difference between distinct objects
that represent the same date).

Top be picky, my opinion on Date doesn't touch on the issue what it
attempts to do; only what it in fact does. As you say, there are
problems with imposing any interpretation of equals on the current core
API, mainly because it's clear that the authors of the API did not share
any common idea of what equals ought to be. I have my guess about the
oldest use of equals, and one that is confirmed by the majority of its
uses in the API, but there are definitely exceptions.

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

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

Chris Uppal

John said:
Chris said:
[As an almost completely irrelevant aside -- I'm not sure that
"immutable" is even a first rank OO concept, at least not in the
hard-line version of OO that I preach. "state" (and hence
[im]mutability) is merely part of the implementation of an object, and
hence not to be accorded importance comparable with that of identity
and behaviour. "state" is just one of the ways that we achieve
behaviour that differs polymorphically between objects and changes
across time.]

What if one could define "immutability" in terms of object behavior?
For instance, "An object is immutable if it enforces the rule that every
behavior it expresses has identical observable results every time it is
invoked with the same inputs." Does that not effectively capture the
concept?

As a purely technical quibble, I think that you'd have to phrase the condition
in terms of the observable results of all possible /sequences/ of behaviour
(method calls). There would be a few other odds and ends to clear up too (e.g.
how to categorise a method that just returns the value of
System.currentTimeMillis(), or what to do about "state" that is actually
held in static fields).

But, quibbles aside, I think that would be a start at capturing the idea of
externally observable state.

The problem that bothers me, though, can be phrased as a question: "the state
of what ?". We can certainly use these techniques to pin down what we mean by
the (externally observable) state of a /system/ of objects, but I don't think
that we are allowed (as it were) to pin down the state to any particular
object. For instance, its easy to set up a system of two objects where it is
clear to an outside observer that /they/ hold some state (they have history
sensitivity) but where there is no legally expressible test that the outside
observer can use to deduce /which/ of the two is actually holding the state
(even if the outside observer knows that one of them is -- which is not true
for objects in general).

If we look inside objects -- at their implementation -- then we can see both a
physical state (the actual values held in their fields) and a logical state
which extends the physical state to include (something like) the state of any
objects that they "own" (e.g. if an object contains a List, the elements of the
list are part of its logical state). But even at that level, the idea starts
to fray a little, for instance if the class holds (as a static field) a Map
mapping from its instances to some "attribute" of those instances, then is the
value of the attribute a part of the instances' state ?

If we stop peeking under the covers, and consider only the observable state,
then we get a nice clear "yes" as the answer to that last question. But,
unfortunately, the very encapsulation that allows us to say "don't know, don't
care" about the implementation of the attribute, also means that we can never
pin down where the state lives.

I suspect that to get further, we'd have to stop treating all behaviours as
equally valid indicators of observable state, and partition methods into those
that were deemed to tell us about that objects' "state" and those that only
told us about the state of the rest of the system. But that has something of
an arbitrary flavour for me.

-- chris
 
C

Chris Smith

Stefan Ram said:
Is there a theory widely accepted as /the OO theory/?

Of course there is one general concept of OO theory. Much is made of
the fact that OO theory isn't deterministic or mathematically based, nor
does it have one authority. It reaches its conclusions by dialogue and
by a process of give-and-take from a variety of different theorists and
ideas. That's certainly the case. Nevertheless, it's far overstating
the case to claim that there is no solid base of universal ideas in OO
theory, and objects having identity is absolutely part of that solid
base. Without it, you have user-defined data types, perhaps, but not
objects.

As for the rest of what you say, I can only answer "yes". I don't
disagree with any of it. You could call the distinction "object vs.
value" as I and others have; or "reference object vs. value object" as
Martin Fowler does, or any number of other terms; but the distinction
tends to remain, and tends to become more and more explicit as the
programmer gains the experience to realize and express the fundamental
differences between these two approaches to modeling concepts from the
domain.

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

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

Chris Uppal

[a very late reply. I don't know if anyone's still interested. But in case
anyone is, I've left the stuff I'm replying to untrimmed]

Chris said:
I'm not sure of this either, probably because I don't understand exactly
what you mean in certain cases.


That sounds like what I'm looking for, and what I think equals(Object)
does most of the time, except in places where that purpose was lost.
I'd add a caveat, though.

While "One True Equality" is an amusing and flashy phrase,

;-)

Oh, that's just how it dresses when it's out on the town. During working hours
it's known as "unconditional equivalence" -- which is much more sober. And
which expresses the idea better. But what the more sober formulation fails to
convey is the sense that we are not talking about "just another" comparison,
but are talking about a kind of comparison that is more important than any
other.

I'd be more
likely to express it as "equality as a java.lang.Object". In other
words, we're looking for a useful concept of equality that applies in
the context of knowing only that you are working with a
java.lang.Object.

Hmm. I'm not at all sure, but you /may/ have misunderstood me completely here.
I'm not, at this point in the argument, interested in java.lang.Object.equals()
at all. I'm not even interested in Java or OO. I'm trying to get at the
concepts that I will later use to /discuss/ what java.lang.Object.equals()
should or does mean.


As others have pointed out, there are other very
useful equalities that are no less deserving of the term; but
Object.equals simply doesn't apply to them because it's a method in
Object.


Okay. I'm not entirely sure I understand what you mean by "allowed to
play that role", but I'll plunge ahead as if I do.

I'll give another example at the end.

Depending on your definition of "immutable", I'd argue that being
"special" certainly does imply immutability. Lets define mutability as
the property that, given an object which exists at time T1, its behavior
is defined at time T2 independently of any events that occur between T1
and T2. I have defined immutability in a way that has no dependence on
state, in order to satisfy your issues elsewhere.

Now, if one of two objects A and B is mutable, then I can tell you a
very definite means of distinguishing A and B, which are otherwise
equivalent. Namely:

1. Make some modification to object A that will affect future behavior.
2. Compare that specific behavior of objects A and B.

And voila, I've distinguished them. By your earlier definition of
"unconditional equivalence", which is that there is no test to
distinguish them, A and B are not unconditionally equivalent.

I disagree. If A and B can be distinguished in that way then they never were
unconditionally equivalent in the first place. I remind you that, at this
stage, I was still talking about the concepts that lie at least one level lower
than the "objects" in OO, but consider some object A. A is clearly
unconditionally equivalent to itself, but that in no way implies that A is
immutable. Please don't take "A is unconditionally equivalent to B" to
include, as part of my definition, any statement at all about object identity
between A and B -- I'm going to be using the concept to try to understand what
we are using object identity /for/ (and also what we are using the equals()
method for).

It occurs to me that you might be using some other definition of
"immutable", specifically related to the question of whether any of the
object's fields may change. In that case, I would agree that
immutability is irrelevant. For example, two self-modifying lookup
trees, which allow only lookups and not insertions or deletions, might
be unconditionally equivalent; but they would still be free to re-
arrange the structure of their trees, for example, to make a second
lookup of the same object faster. Then I'd invent some other condition
(call it "changeability", perhaps) and define it as I've proposed for
immutability above, and insist on that instead.


Clearly, a) is not an option, however, since such a thing is not
provided by the virtual machine. That leaves us with b) and c).

I like your wording for c) by the way; it very well captures the nature
of the issue; namely, that any number of Java-language objects may
exist, but that there is still only one conceptual entity in existence.


In a sense, they are implementation techniques. However, Java does not
provide enough of an abstraction layer to ensure that they really are
hidden within the implementation. Therefore, they are implementations
of the concept, but not "implementation" in the sense of "something a
client need not care about"... at least in Java. I am assuming that's
what you meant.

Incidentally, that latter fact is almost entirely due to Java having
provided an == operator that guarantees a test for reference equality
and a System.identityHashCode() that can be called on arbitrary objects;
instead of both being exclusively inherited as an implementation from
java.lang.Object. I am becoming increasingly convinced, over time, that
this was a mistake on James Gosling's part, though an understandable one
at the time it was made.

Yes, I agree. (In fact I arrived at a similar conclusion in one of my
"digressions".)

This is, indeed, true. However, it doesn't conflict with requiring
immutable types when overriding equals in Java. Using your technique
b), I don't see why Object.equals would be overridden.

No, it wouldn't be overriden; there's no need, since the default implementation
of equals() already does an identity comparison. Or, if we accept the
compromise that the "mistake on James Gosling's part" imposes on us, the
programmer would naturally use == for the comparison because s/he "knows" that
object identity is the correct comparison operation given the implementation of
"special"ness that is in use.

I don't see where you're getting point b here. Can you explain?

I'm sorry, I don't think I can. The post you were replying to was my best shot
at explaining. I don't think I can do better -- at least not until I've
thought about it more. But maybe my new example will help clarify what I
mean.

Top be picky, my opinion on Date doesn't touch on the issue what it
attempts to do; only what it in fact does. As you say, there are
problems with imposing any interpretation of equals on the current core
API, mainly because it's clear that the authors of the API did not share
any common idea of what equals ought to be. I have my guess about the
oldest use of equals, and one that is confirmed by the majority of its
uses in the API, but there are definitely exceptions.

(Not in reply to the above paragraphs; the remainder is the example I mentioned
above.)

Let's suppose we want to think about coloured points on a (quantised) plane.
That's a /specific/ plane so each x/y co-ordinate pair denotes a specific point
on that plane. Each point is coloured, and each point has exactly one colour
(at any given time).

Now -- /before/ we leap into considering how to represent this as objects -- I
note that these points are "special" in my sense. Specifically, any two points
that have the same x and y co-ordinates must be "unconditionally equivalent"
(that's just a re-statement of the problem definition). In particular they
must have the same colour. And, what's more, they must continue to have the
same colour (as each other) as time passes.

So, we have to find a way to map those semantics onto Java's OO. Specifically
how to map the "special"ness and the concept of "unconditional equivalence" for
coloured points, onto Java objects, operations, and methods. We can consider
at least four candidate implementations, three corresponding to my (a), (b),
and (c), and a fourth which has occurred to me since my earlier post.

a) Implement it directly at the VM level. Not possible without changing JVM
semantics. And not interesting here.

b) Enforce uniqueness. In this case we decide that, by design, no two
instances of our ColouredPoint class shall ever have the same co-ordinates. To
do this we keep some kind of registry of existing objects somewhere (say as a
weak Collection in the class). and (probably) forbid the direct use of "new".
In this case we map the "unconditional equivalence" comparison onto ==, and our
jiggery-pokery with the object registry allows us to be sure that "special"ness
is correctly maintained. Notice that in this case our ColouredPoints can
change colour without any problems at all -- if any two ColouredPoints are
unconditionally equivalent, then (by the design of the implementation) they
must be the same object, and so if one changes colour so does the "other".

c) Override equals(). In this case we change the equals() method so that it
considers any two ColouredPoints to be equal() iff they have the same x and y
co-ordinates. For this to work at all, the ColouredPoints /must/ be
immutable[*] -- they must never change colour -- but that's a characteristic of
our implementation technique, not something that is implicit in the concepts
that we are attempting to model. (Although there is some tie-in, since if the
concept happened to /require/ mutability, then this implementation technique
would not be applicable.) Note that this technique is -- when expressed in
Java -- a bit weak in its representation of "unconditional equivalence" since
there are some Java operations that allow object identity to "leak" out despite
our override of equals(), and so we have an slightly imperfect representation
of "special"ness which programmers have to be aware of. But if that's not a
/big/ problem (which it probably wouldn't be in this example), then
implementation technique may be preferable (despite those limitations) to the
housekeeping overhead involved in using technique (b).

d) Override equals() as before, but in this case, instead of storing the colour
as part of each ColouredPoint's physical state, we have a map somewhere from
equivalence classes of ColouredPoints to Colours. That could be stored as a
class-side field (a bit like the registry in (b)), but in this example it would
probably be better to introduce a new object that represents the specific
surface that the points are on. Note that ColouredPoints could be logically
mutable, since they could implement both getColour() and setColour() (both
internally delegating to the Surface). In this case we have represented
"unconditional equivalence" as equals(), and have ensured that (with some
leaks) "special"ness is maintained -- even though our points can change colour.

I don't know whether you take immutability to be part of what "value semantics"
means (I do), but it you do, then technique (d) illustrates a case where the
ColouredPoints /don't/ have value semantics, because they are (logically)
mutable, but they /do/ correctly override equals().

-- chris

([*] a necessary but not sufficient condition, since we'd also need code to
ensure that one couldn't just "brew up" an instance that had the same x/y as
another instance but a different colour)
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top