Maybe I'm missing something, but ....
Give a Progress.Lang.Object the ToString() method will return the fully qualified name followed by understcore followed by a unique object identifier.
This would appear to be a handy way to tell whether object A and object B were actually the same object, except ...
ToString() is a method name which is likely to be overriden somewhere else in the class hierarchy. Give that the context in which I want this information is when the object is in a temp-table, i.e., it is just a PLO, I can't define some other property, e.g., a GUID, to uniquely identify the object because the method would not be available without casting.
Am I missing something?
tamhas wrote:
Maybe I'm missing something, but ....
Give a Progress.Lang.Object the ToString() method will return the fully qualified name followed by understcore followed by a unique object identifier.
This would appear to be a handy way to tell whether object A and object B were actually the same object, except ...
ToString() is a method name which is likely to be overriden somewhere else in the class hierarchy. Give that the context in which I want this information is when the object is in a temp-table, i.e., it is just a PLO, I can't define some other property, e.g., a GUID, to uniquely identify the object because the method would not be available without casting.
Am I missing something?
Why would you use ToString() for comparison purposes? There's an Equals() method on PLO which seems appropriate. It does a reference comparison in the absence of any overrides (ie 'handle to handle' if you will). A
You can do INT(objRef) which gives you an integer value of the reference, and you can also do a reference-to-reference comparison if you choose. This is nice in temp-tables because it's an indexed value. But it only works if you're doing identity matches, and not equality matches. In the latter case, you must use Equals().
-- peter
Why would you use ToString() for comparison purposes? There's an Equals() method on PLO which seems appropriate. It does a reference comparison in the absence of any overrides (ie 'handle to handle' if you will). A
You can do INT(objRef) which gives you an integer value of the reference, and you can also do a reference-to-reference comparison if you choose. This is nice in temp-tables because it's an indexed value. But it only works if you're doing identity matches, and not equality matches. In the latter case, you must use Equals().
-- peter
Wait... So INT(objRef) gives you the equivalent of hPersistenProc:HANDLE? So if I was looking for the equivalent of java.lang.Object.hashCode, this would be it?
So INT(objRef) gives you the equivalent of hPersistenProc:HANDLE?
Yes, but without the WIDGET-HANDLE() equivalent.
So if I was looking for the equivalent of java.lang.Object.hashCode, this would be it?
No - my understanding of what HashCode is/does is that it's more complex that that, and that it's more closely aligned with the equality side of things, rather than the identity.
-- peter
So if I was looking for the equivalent of java.lang.Object.hashCode, this would be it?No - my understanding of what HashCode is/does is that it's more complex that that, and that it's more closely aligned with the equality side of things, rather than the identity.
-- peter
From the Javadoc on hashCode():
public int hashCode()
java.util.Hashtable
.The general contract of hashCode
is:
hashCode
method on each of the two objects must produce the same integer result. equals(java.lang.Object)
method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables. As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
equals(java.lang.Object)
, Hashtable
So, as I understand it, the default version of hashCode returns you an integer representation of the address. The first bullet and last statement sound exactly like the behavior of a OpenEdge handle.
The difference is, though, that hashCode() can be overridden in a Java class. So I guess the question I am really asking is can I use INT(obj) to produce the same value as the default implementation of hashCode? And I think what you are saying is "Yes".
I agree that the last point seems to indicate that I'm saying 'yes', but I am really saying no.
Exhibit the First:
Whenever it is invoked on the same object more than once during an
execution of a Java application, the hashCode method must
consistently return the same integer, provided no information used
in equals comparisons on the object is modified.
def var o1 as Progress.Lang.Object.
def var o2 as Progress.Lang.Object.
o1 = new Object().
o2 = new Object().
message int(o1) eq int(o2). /* will be false */
The example above is a little unfair, since PLO is somewhat special. If you substitute the class below and run it, the object references will be unequal, while calling Equals() will return true, and the HashCode values will be the same.
class foo:
def pub prop Value as char no-undo initial 'a value' get. set.
method public int HashCode():
iHash = TurnIntoHash(this-object:Value). /* TurnIntoHash to be implemented */
return iHash.
end method.
method override public logical Equals(po as Object):
return (po:Value eq this-object:Value).
end.
end class.
Exhibit the Second:
If two objects are equal according to the equals(Object) method,
then calling the hashCode method on each of the two objects must
produce the same integer result.
-- peter
I think we're on the same page. In summary:
Now, of course, you know what the next question is going to be...
When do we get hashCode() on PLO? Frankly, I was really surprised it wasn't included as part of the base hierarchy from the beginning.
(what an unfortunate acronym for
Progress.Lang.Object. I can just see Yasser Arafat turning over in
his grave).
Tmh coined "PABLO" for Plain ABL Object, which I quite like. I've also heard POAO (Plain Old Abl Object). And you can certainly roll your own.
-- peter
Whenever it is invoked on the same object more than once during an
execution of a Java application, the hashCode method must
consistently return the same integer, provided no information used
in equals comparisons on the object is modified.
def var o1 as Progress.Lang.Object.
def var o2 as Progress.Lang.Object.
o1 = new Object().
o2 = new Object().
message int(o1) eq int(o2). /* will be false */
I think I misread this bullet: int(o1) will always equal int(o1), so in that sense, yes, it does apply.
However, I would say that this should be made explicit via a HashCode method on Progress.Lang.Object since it's then also overridable, and not an obscure artifact. Enhancement requests are always welcome
-- peter
And not subject to
tail -f enhancement-request | /dev/null
?
Regardless of what Java does .... (WWJD = What Would Java Do?) ... it seems to me that one wants two quite different things.
One is to be able to tell whether one particular object reference is actually the same object as some other object reference. Int(ObjRef) seems to do just that.
The other is to determine whether two objects are "equal" where "equal" is defined by the programmer.
As I read the doc, the default meaning of Equals is true only if the two references point to the same object. This is counter to my intuition which would be that Equals would be true for two objects of the same type with the same state. One could, of course, override this in every object, but that seems chancy, so really Equals is nothing more than an alternative expression for Int(ObjRef) = Int(ObjRef).
Thus, to achieve the second goal, we are going to have to provide some new property that is implemented on all objects.
As I read the Javadoc, hashcode is shorthand implementation for the equals check as I have defined it above, *not* a shorthand for checking object identity. I.e., hashcode varies by state, is programmer defined, and two separate objects with the same state (as defined by the programmer) have the same hashcode..
I think you were right the first time, Peter. As I am reading that int(o1) at time T1 will always equal int(o1) at time T2, but for Java hashcode, that won't be the case if the state has changed between T1 and T2.
I disagree with you about equals.
Equals defaults to the IDs matching, however, as a programmer you can override equals to mean whatever you want it to mean. The same is true with hashCode.
Yes, but what I am saying here is that Java isn't giving me what I as a programmer would really want. What I want equals to be is equal state, regardless of whether it is the same object.
I.e., there should really be a property like ID or HANDLE where A.ID = B.ID if A and B are actually the same object. We get this, less attractively, with Int(A) = Int(B).
Equals defaulting to be this same thing is not giving us anything extra.
What we really want separate from this IsSameObject test is HasSameState. Default should be that all data members match in value, but I can override this if desired to exclude some data members or to add some fuzz around "match" or whatever. Given its default, Equals() doesn't do this at all and is redundant on the Int(A) = Int(B) test.
tamhas wrote:
I think you were right the first time, Peter. As I am reading that int(o1) at time T1 will always equal int(o1) at time T2, but for Java hashcode, that won't be the case if the state has changed between T1 and T2.
Only if you alter the default implementation of HashCode().
What we really want separate from this IsSameObject test is HasSameState. Default should be that all data members match in value, but I can override this if desired to exclude some data members or to add some fuzz around "match" or whatever. Given its default, Equals() doesn't do this at all and is redundant on the Int(A) = Int(B) test.
IsSameObject = identity: int(o1) eq int(o2)
HasSameState = equality: o1:Equals(o2)
Equals() should not be a constant: it should take the current state into account. I would submit, as should HashCode (of course, if you're using it as a key, you'd probably want an immutable object otherwise you're kinda up a certain creek without a paddle).
I bookmarked this as interesting: http://www.ibm.com/developerworks/java/library/j-jtp05273.html#write
-- peter
tail -f enhancement-request | /dev/null
Is that your experience from the inside? :-o
Depends,on your point of view. To me, if equals() defaults to meaning that it is the same object, but equals is overridable, then equals becomes meaningless if not overriden and unreliable unless consistently overriden to mean equal state. In the same sense, if hashcode is not overriden to include state, then I don't see that it has a consistent and useful meaning beyond int(OR).
I.e., it doesn't seem to me that Java is giving us a useful model here for fulfilling the two goals I set out. If the default implementation of all of the options only tells us about object identity, then not only are they redundant, but they are unreliable because, being overridable, they can't even be consistently used to tell us about object identity.
Seems to me that the need is for a consistent, reliable test of object identity PLUS a test that two separate objects are identical in programmer defined terms. And, the default for the latter should be all state for the object.
Not at all... I have seen PSC implement stuff that I really wish they hadn't because customers drove it!
I also know that unless you make a really good case for something, it will probably be ignored, which is the way it should be. The problem is, I am having a hard time articulating what my case for this is right now. I need to sit down and justify it properly before I submit the enhancement request.
And, yes, this is low-hanging fruit, so it should be the simplest thing in the world to implement.
After some off list exchange, let me bring this back with some clarification.
As I have said, what I would like to have is two things
1) The ability to tell that two object references actually point to the same object.
2) The ability to tell that two object references point to objects which have the same state, regardless of whether they are the same object. I.e., one is a clone of the other.
The first one we have in ABL with int(O1) = int(O2) ... it just isn't very elegant. It would be nice if PSC would implement a property one PLO called ID or Handle or something which was int(self) so we could make the comparison more nicely. Yes, I could create Son of PLO as a base class for all my classes, but unless there was something else interesting to put in there that didn't belong in PLO, I would rather have it in PLO.
The Java versions of the second one, Equals and Hashcode, are unappealing to me because their default behavior is that two objects are only equal if they are in fact the same object. Not only is this redundant with int(o), but it implies that, for these members to mean anything else, I have to override the behavior in EVERY CLASS! Maybe I'm lazy, but I like default behaviors to be the behavior I actually want most of the time so that I only have to override to get a different behavior.
What I would like for a default is for a function that would compare all the values of all of the data members and tell me if they all matched. Implemented directly, that would take rather stronger reflection than we currently have available. However, IF PSC were to give us WRITE-XML on an object which would create an XML string containing the values of all the datamembers ... which, of course, would be a lovely, lovely tool for serializing objects for transmission over the wire and capturing the state of an object for possible rollback and such (Memento pattern) and putting an object into a database and all sorts of other useful things ... then something like
SHA1-DIGEST(self:WRITE-XML)
would provide us with a great basis for making that comparison.
So, ideal would be to have PSC implement WRITE-XML and READ-XML on objects and then to define something like Hashcode as a property on PLO which did the SHA1-DIGEST(self:WRITE-XML) computation ... and think how lovely that would be. Not the same as the Java hashcode, but better.
The first one we have in ABL with int(O1) = int(O2) ... it just isn't
very elegant. It would be nice if PSC would implement a property one
PLO called ID or Handle or something which was int(self) so we could
make the comparison more nicely.
IMO, the reference itself shouldn't really be exposed. PLO:Equals() can do this comparison for you, and if you want to do that check first in your Equals() - should you have one - you can. But you should really be calling super:Equals(): that's what it's there for. It should encapsulate the equality match.
Not only is this
redundant with int(o), but it implies that, for these members to mean
anything else, I have to override the behavior in EVERY CLASS! Maybe
I'm lazy, but I like default behaviors to be the behavior I actually
But you don't want the default behaviour ... you want YOUR behaviour (per class). So I don't get what the problem is.
What I would like for a default is for a function that would compare
all the values of all of the data members and tell me if they all
matched.
All the members? That's analogous to one DB record only being equal to another if and only if all of the fields are identical. There's a reason we have primary unique keys.
-- peter
The problem with relying on PLO:Equals() is that it is overridable, is it not? So, if what I really want to know is whether two object references are pointing to the same thing, Equals is not reliable. That's why I want an immutable property ... or for Equals to be FINAL.
Also, one can store PLO.ID, but one has nothing to store with PLO.Equals() except the boolean result, which tells you nothing later.
But you don't want the default behaviour ... you want YOUR behaviour (per class). So I don't get what the problem is.
What I want, in general, is a way to tell if one object is effectively a clone of another.
In some cases, I might want to exclude some property from that comparison. E.g., if I had a class in which there was a datetime stamp for when it was created, I might want to exclude that from the test.
In the Java, Equals and Hashcode default behavior tells me that two references are the same ONLY if they are the same object. Thus, in order to get a comparison of values, I have to override EVERY class.
The ABL Equals has the same default behavior as the Java Equals and thus the same defect. Moreover, if I do override it in every class to give my desired behavior, then I lose its potential value for checking for the same instance.
All the members? That's analogous to one DB record only being equal to another if and only if all of the fields are identical. There's a reason we have primary unique keys.
Yes, all the members. The equivalent of unique key constraints is a different property altogether.
In fact, think about it in terms of the kind of update cycle that one might do with a DB record. I start with an object and it has a particular state, corresponding, perhaps, to all the fields in a particular database record. I then clone it or make a memento to record its before image state. Then I do something to it ... e.g., send it to the UI for processing or some BL action. When I get it back, it stil has the same identity it started out with. If I am using arbitrary keys like a GUID as a primary index, it still has that same value. But, has it changed from what it was when it started out. A comparison with the clone or memento will tell me whether it has changed or not. Equals() does nothing for me here.
And, hey, whether or not you would do things this same way, you have to admit that WRITE-XML and READ-XML on all data members of a class would be very cool for a lot of purposes and a property equivalent to SHA1-DIGEST(self:WRITE-XML) would be a great way to get a value for an object that would tell you whether or not it had changed and whether or not a possible clone was or was not a true clone. Seems like both of these should be very easy to add. 10.2B02 maybe?
One additional thought. If one serializes an object and then stores it, sends it across the wire, or whatever and then at some later point reconstitutes it, the int(ObjRef) will be different from the original. But, if one has done something like pass this object to the client and the client has passed it back, one would want a way to connect the new object with the original. One way to do this would be to have a property like GUID which was included in the serialization and thus could be set if the object was reconstituted from a serialized form, i.e., a private setter. This seems like another trivially easy and useful property for PLO.