Following on from this question - how do you explain to someone that this is just crazy!:
boolean someMethod(Map<String, Object> context) {
Object object = context.get("someProperty")
Object another = context.get("anotherProperty")
return object.toString().equals(another.toString());
}
Apparently the reason for why Object.equals(...)
is not used is that "what's contained in the Map is not concretely known, but is definitely known to be one of the primitive wrappers i.e. String
, Double
, Boolean
, etc... and that Boolean.TRUE
is required to be equal(...)
to the String
"TRUE"".
4 Answers 4
It's wrong because there is no contract (not even an informal expectation!) for toString()
that guarantees that any subclass of Object
will define it in a way compatible with an equivalence relationship (read the javadoc for Object.equals()
to see what I mean).
It may work for a class where you know that toString()
is compatible with equals
, but this is not the case for the values in Map<String, Object>
!
For an obvious example where almost certainly the method doesn't do what you would expect, imagine a subclass of Object
that does not override toString()
. In that case, the String representation of your instance is a function of the class name and the object's hashCode. Almost certainly not what you want to use to test for equality.
-
Any comments on this part of the question? >> "what's contained in the Map is not concretely known, but is definitely known to be one of the intrinsic wrappers i.e. String, Double, Boolean, etc... and that Boolean.TRUE is required to be equal(...) to the String "TRUE""auser– auser2012年05月17日 19:24:36 +00:00Commented May 17, 2012 at 19:24
-
When in Rome, do as the Romans do: Java is a strong, static-typed language. Boolean.TRUE may be equal (IDK) to String "TRUE", but this is duck typing at hand and considered bad practice in Java. Also, what's guaranteed is that what's contained in the map is
Object
. Assuming otherwise is a bad idea, again because of the Java paradigms. Reminds me of "You can write Fortran in any language".K.Steff– K.Steff2012年05月17日 20:37:54 +00:00Commented May 17, 2012 at 20:37 -
@loudsight What do you mean, "intrinsic wrappers"? In any case,
String
,Boolean
andDouble
all have well-definedequal
methods, which you might as well use (though I'm not sure it's numerically safe to useDouble.equals()
, but that's another issue). In the general case,toString()
is not meant for equality tests, so don't use it for that.Andres F.– Andres F.2012年05月17日 22:32:09 +00:00Commented May 17, 2012 at 22:32 -
Actually, I meant to say primitive rather than instrinsic. en.wikipedia.org/wiki/Primitive_wrapper_classauser– auser2012年05月18日 17:13:42 +00:00Commented May 18, 2012 at 17:13
-
@loudsight Java primitive wrappers all have well-defined
equals
methods. You absolutely do NOT need to convert to String.Andres F.– Andres F.2012年05月18日 17:21:57 +00:00Commented May 18, 2012 at 17:21
It's just crazy, because it can go wrong, because string representations are not canonical:
Double zeroDouble = new Double(0);
Integer zeroInteger = new Integer(0);
zeroDouble.doubleValue() == zeroInteger.doubleValue(); // true
zeroDouble.toString().equals(zeroInteger.toString()); // false ("0.0" vs "0")
File file1 = new File("file.txt");
File file2 = new File("FILE.TXT");
file1.equals(file2); // true in windows, false in unix
file1.toString().equals(file2); // false ("file.txt" vs. "FILE.TXT")
-
1Actually,
zeroDouble.equals(zeroInteger)
is false.waxwing– waxwing2012年05月17日 10:36:14 +00:00Commented May 17, 2012 at 10:36 -
Oops, I stand corrected. Numerical comparisons are almost never trivial :)Joonas Pulakka– Joonas Pulakka2012年05月17日 12:41:00 +00:00Commented May 17, 2012 at 12:41
The indicated method would be very odd as part of a public API. Whether or not it is crazy as a private or protected part of a class would depend upon how it was being used. The method appears to encapsulate two different quirky aspects, either of which could plausibly be justifiable in some contexts but not others; while it would seem unlikely that both would be justifiable, there's no particular reason why they couldn't be.
The first quirky aspect is the fact that the method is searching for particular fixed strings within the maps. Such behavior is generally ugly (and it would probably be better if the strings were defined as constants) but could be justifiable if:
- The map is never exposed to code outside the package.
- It is necessary to use some collection type to store aggregates, each of which consists of a mapping plus a few other bits of information).
- The use of the encapsulated mapping is such that no key could possibly conflict with the strings that are used for the "few other bits of information".
- The mapping is used so much more than the other bits of information stored within the advocate that using a class which contained a
Map
along with the other bits of information would be much more cumbersome than using a map which has a few bits of information associated with magic keys.
Some languages and frameworks would provide alternate means of attaching supplemental information to a type, but the approach indicated here is sometimes the most practical one given Java's type system.
The second ugly aspect is that the code is comparing two objects' ToString
values as a means of comparing the objects. In most cases, either the objects will be strings (in which case one could just compare them), or they would be some other type which has some attribute of type String
such a name, in which case one should cast the objects to that other type, call GetName
on both instances, and compare the resulting strings. If, however, the majority of table entries would only need to store strings, but a few might need to have an entry which combines a string with a small amount of supplemental information, it may in some cases be easier to store things in the table as type Object
with the expectation that every entry will either be a String
, or be some known kind of object. Since Java's type system has no concept of "thing which will either be type1 or type2", using Object
, though ugly, may be the only practical alternative in cases where one of the types is something like String
which would share no common ancestor--other than Object
--with any user class.
If you are keenly interested to know about Object Equality then use can use the method of class Object.
int hashCode() gives the integer value called hashCode which pertain to an object.
definitely the hashcode differs object to object.
it's a easiest way to identify object equality.
-
There is no guarantee that equal objects have the same hashcode, and similarly there is no guarantee that non-equal objects have differing hashcodes.RonU– RonU2012年05月17日 16:28:07 +00:00Commented May 17, 2012 at 16:28
-
2This is a serious misunderstanding of what
hashCode
does.Andres F.– Andres F.2012年05月17日 21:31:37 +00:00Commented May 17, 2012 at 21:31