I'm going through a book on data structures. Currently I'm on graphs, and the below code is for the vertex part of the graph.
class Vertex<E>{
//bunch of methods
public boolean equals(Object o){
//some code
}
}
When I try to implement this equals method my compiler complains about not checking the type of the parameter and just allowing any object to be sent it. It also does seem a bit strange to me why that parameter shouldn't be a Vertex instead of an Object. Is there a reason why the author does this or is this some mistake or antiquated example?
#Override
public boolean equals(Object obj)
{
if (!(obj instanceof Vertex)) return false;
else return // blah blah
}
equals(Object) is the method defined in the root - Object. If you don't match the signature exactly, Object's version will be called when someone checks if two objects are equal. Not what you want.
You've probably seen other methods (like Comparator) where you can use the exact time. That's because those APIs were generic-ified with Java 5. Equals can't be because it is valid to call equals with two separate types. It should return false, but it is valid.
equals is a method inherited from Object, is defined to be flexible enough so that you can take any object and test if it is equal to any other object (as it rightfully should be able to do), so how could it be any other way?
Edit 1
Comment from jhlu87:
so is it not good form to write an equals method that has an input parameter of vertex?
You are welcome to create your own overload to any method, including equals, but doing so without changing the name could risk confusing many who would assume that your equals is the one that inherits from Object. If it were my code and I wanted a more specific equals method, I'd name it slightly different from just "equals" just to avoid confusion.
If your method doesn't take an argument of type Object, it isn't overriding the default version of equals but rather overloading it. When this happens, both versions exist and Java decides which one to use based on the variable type (not the actual object type) of the argument. Thus, this program:
public class Thing {
private int x;
public Thing(int x) {
this.x = x;
}
public boolean equals(Thing that) {
return this.x == that.x;
}
public static void main(String[] args) {
Thing a = new Thing(1);
Thing b = new Thing(1);
Object c = new Thing(1);
System.out.println(a.equals(b));
System.out.println(a.equals(c));
}
}
confusingly prints true for the first comparison (because b is of type Thing) and false for the second (because c is of type Object, even though it happens to contain a Thing).
It's because this method existed before generics, so for backward compatabitity it has to stay this way.
The standard workaround to impose type is:
return obj instanceof MyClass && <some condition>;
It is because the author is overriding equals. Equals is specified in java.lang.Object and is something that all classes inherrits from.
See the javadoc for java.lang.Object
Related
This question already has answers here:
Java generics type erasure: when and what happens?
(7 answers)
Closed 7 months ago.
Given a Set<X>, I want to write a method that acts differently depending on the class X is. In actuality, I'd have 2 cases: A or B.
The method looks like:
public<X> boolean myMethod(Set<X> mySet)
{
// if X is actually the class A
...
// if X is B
...
}
I am not sure how to check this. I tried X::getClass, but it wouldn't let me use equals method.
In a general sense, this is not possible
Due to type erasure, a Java Set does not have a generic type at runtime. Generics act as compile-time checks on the objects passed into methods, but consequently the specific values are not available at runtime. A Set<String> is the same class as a Set<Map<Integer, Thread>> and the same class as a bare Set.
Potential workaround with class parameter
If you really need to know this, you could change the signature of your method to be:
public <X> boolean myMethod(Set<X> mySet, Class<X> clazz)
Now you have a Class instance available at runtime, which is guaranteed to be compatible with X. This you can introspect (e.g. String.class.isAssignableFrom(clazz)).
There are two drawbacks to this approach: firstly, you'll have to explicitly pass in the extra parameter each time the method is called. Secondly, if A and B can be subclasses of one another, this is not going to work; you could pass in e.g. B.class even when the generic parameter was A.
However, this feels like a code smell
You shouldn't be trying to do fundamentally different things based on a generic parameter. Instead of switching behaviour in your method here, do something different within the classes A and B:
public<X> boolean myMethod(Set<X> mySet)
{
boolean result = false;
for (X x : mySet) {
result &= x.doSomething();
}
return result;
}
class A implements MyDoSomethingInterface {
public boolean doSomething() {
// Here you put the logic for "if X is A"
}
}
class B implements MyDoSomethingInterface {
public boolean doSomething() {
// Here you put the logic for "if X is B"
}
}
And if your reaction is that this won't work because A and B are built-in classes, you'll need to wrap them in your own domain objects (which is the correct approach anyway because you want to associate this extra behaviour with them).
I suggest you take one object from set and check class of single object
like below, please check
public<X> boolean myMethod(Set<X> mySet)
{
Object tmpObj = null;
for(Object obj : set){
tmpObj = obj;
break;
}
if(tmpObj instanceof A){
// if X is actually the class A
}else if(tmpObj instanceof B){
// // if X is B
}
}
When we override the equals() method in Java, I know that Object needs to be a parameter, but I wonder - why Object?.
Second, let us say we override hashcode() and implement equals(), but set the parameter in equals() to MyClass instead of Object (MyClass being the class whose equals() method we override). Will we still get the expected behavior if we use HashMap?
Update: Yes, it will be overloading instead of overriding. But what will happen if we use HashMap with overloaded equals()? Also, I don't find the answer in related posts. Or is it something obvious that I am missing?
If you write an equals() method whose parameter is not Object, you are overloading the method, not overriding it.
Now, as for HashMap - HashMap calls equals to compare keys. The type of the compared keys is Object. Therefore, if you define an equals() method with a parameter whose not Object, this method will be ignored by HashMap.
I tried the following code :
public class SomeClass
{
int privateMember;
// note it's important to override hashCode, since if the hashCode of two
// keys is not the same, equals() won't be called at all
public int hashCode ()
{
return privateMember;
}
public boolean equals (Object other)
{
if (other instanceof SomeClass) {
return this.privateMember==((SomeClass)other).privateMember;
}
else {
return false;
}
}
public static void main(String[] args)
{
HashMap<SomeClass,String> map = new HashMap<SomeClass,String>();
SomeClass s1 = new SomeClass ();
SomeClass s2 = new SomeClass ();
s1.priv=4;
s2.priv=4;
map.put (s1, "something");
if (map.containsKey (s2)) {
System.out.println ("found!");
} else {
System.out.println ("not found!");
}
}
}
This code outputs "found!".
Now, if you run the exact same code, but replace the equals method with :
public boolean equals (SomeClass other)
{
if (other instanceof SomeClass) {
return this.privateMember==((SomeClass)other).privateMember;
}
else {
return false;
}
}
The output will be "not found!", which means our equals method was ignored.
The collections use the equals and hashcode methods from the Object base class. Therefore you must override them in order for your custom class to provide an implementation. You can overload equals if you wish, and that would work for situations where some code knows that it's dealing with an instance of MyClass. However, this would be misleading.
All the collections classes are designed to work with instances of Object and Object provides a general purpose equals method.
You shouldn't really need to write an equals method directly. You can either generate one using your IDE, or use EqualsBuilder from Apache Commons (https://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/builder/EqualsBuilder.html) to help put it all together.
It is stated in Object's .equals(Object) javadoc:
It is symmetric: for any non-null reference values x and y,
x.equals(y) should return true if and only if y.equals(x) returns
true.
Almost everywhere in example code I see overridden .equals(Object) method which uses instanceof as one of the first tests, for example here: What issues / pitfalls must be considered when overriding equals and hashCode?
public class Person {
private String name;
private int age;
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!(obj instanceof Person))
return false;
...
}
}
Now with class SpecialPerson extends Person having in equals:
if (!(obj instanceof SpecialPerson))
return false;
we con not guarantee that .equals() is symmetric.
It has been discussed for example here: any-reason-to-prefer-getclass-over-instanceof-when-generating-equals
Person a = new Person(), b = new SpecialPerson();
a.equals(b); //sometimes true, since b instanceof Person
b.equals(a); //always false
Maybe I should add in the beginning of SpecialPerson's equals direct call to super?
public boolean equals(Object obj) {
if( !obj instanceof SpecialPerson )
return super.equals(obj);
...
/* more equality tests here */
}
A lot of the examples use instanceof for two reasons: a) it folds the null check and type check into one or b) the example is for Hibernate or some other code-rewriting framework.
The "correct" (as per the JavaDoc) solution is to use this.getClass() == obj.getClass(). This works for Java because classes are singletons and the VM guarantees this. If you're paranoid, you can use this.getClass().equals(obj.getClass()) but the two are really equivalent.
This works most of the time. But sometimes, Java frameworks need to do "clever" things with the byte code. This usually means they create a subtype automatically. Since the subtype should be considered equal to the original type, equals() must be implemented in the "wrong" way but this doesn't matter since at runtime, the subtypes will all follow certain patterns. For example, they will do additional stuff before a setter is being called. This has no effect on the "equalness".
As you noticed, things start to get ugly when you have both cases: You really extend the base types and you mix that with automatic subtype generation. If you do that, you must make sure that you never use non-leaf types.
You are missing something here. I will try to highlight this:
Suppose you have Person person = new Person() and Person personSpecial = new SpecialPerson() then I am sure you would not like these two objects to be equal. So, its really working as required, the equal must return false.
Moreover, symmetry specifies that the equals() method in both the classes must obey it at the same time. If one equals return true and other return false, then I would say the flaw is in the equals overriding.
Your attempt at solving the problem is not correct. Suppose you have 2 subclasss SpecialPerson and BizarrePerson. With this implementation, BizarrePerson instances could be equal to SpecialPerson instances. You generally don't want that.
don't use instanceof. use this.getClass() == obj.getClass() instead. then you are checking for this exact class.
when working with equalsyou should always use the hashCode and override that too!
the hashCode method for Person could look like this:
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
and use it like this in your equals method:
if (this.hashCode() != obj.hashCode())
{
return false;
}
A type should not consider itself equal to an object of any other type--even a subtype--unless both objects derive from a common class whose contract specifies how descendants of different types should check for equality.
For example, an abstract class StringyThing could encapsulate strings, and provide methods to do things like convert to a string or extract substrings, but not impose any requirements on the backing format. One possible subtype of StringyThing, for example, might contain an array of StringyThing and encapsulate the value of the concatenation of all those strings. Two instances of StringyThing would be defined as equal if conversion to strings would yield identical results, and comparison between two otherwise-indistinguishable StringyThing instances whose types knew nothing about each other may have to fall back on that, but StringyThing-derived types could include code to optimize various cases. For example, if one StringyThing represents "M repetitions of character ch" and another represents "N repetitions of the string St", and the latter type knows about the first, it could check whether St contains nothing but M/N repetitions of the character ch. Such a check would indicate whether or not the strings are equal, without having to "expand out" either one of them.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why .equals method is failing on two same value objects?
This is really simple but I'm obviously missing something pretty big here.
Cat cat1 = new Cat("bob");
Cat cat2 = new Cat("bob");
System.out.println(cat1 == cat2);
//false since these references point to diferent objects
System.out.println(cat1.equals(cat2));
//returns false also??
Cat is just a simple class that only has a name.
What is going on here, how does equals() work? I was under the impression that it compared all the fields of the object. It seems that is not the case.
Do I need to overide it for all my classes?
Yes.
java.lang.Object provides very basic implementations of equals() and hashCode(). In particular, they don't go around reflecting on the type of the instance, which would (1) be dreadfully slow, and (2) carry a significant risk of comparing fields that you for various reasons don't want to compare in an equality comparison.
If you want equals() and hashCode() to actually be useful for comparing value equality (rather than reference equality which == does), you'll need to implement both within your own type.
Note that it's not enough to implement just equals(); while technically that will "work", it has the potential to lead to all kinds of weirdness. The simple rule of thumb is: neither or both, but never only one. And they must work on the same fields; if equals() says two instances are equal, then calling hashCode() on both must return the same value (also see the hashCode() contract).
It's also usually a good idea to override toString() with code to provide a meaningful description of the object in question. While not strictly needed, you only need to hit your head against this once in the debugger to realize the value. (Thanks #JonTaylor for mentioning this highly useful, related tidbit.)
And it's .NET that calls it GetHashCode(), while Java uses only hashCode() as the function name...
You need to override equals inside your Cat class. Default equals compares objects on references.
class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Cat))
return false;
Cat c = (Cat) obj;
return this.name == null ? false : this.name.equals(c.name);
}
#Override
public int hashCode() {
return this.name == null ? 31 : this.name.hashCode();
}
#Override
public String toString() {
return "Cat Name :" + name;
}
}
References
equals
hashCode
toString
The equals() provided by java.lang.object compares, simply speaking, a unique identifier for the object, though not entirely accurate you can think of it as a memory location, so it will only be true if you compare an object with itself (i.e. two references to the same object in memory)
You need to implement your own equals() method in your Cat class:
class Cat
{
String name;
#Override
public boolean equals(Cat other)
{
if (this.name.equals(other.name))
return true;
return false;
}
}
It would be wise to override hashCode() also, unless this is just a very basic application for homework or something. Also toString() can be useful to override as well.
http://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html
From [Java Doc]
The equals method for class Object implements the most discriminating
possible equivalence relation on objects; that is, for any non-null
reference values x and y, this method returns true if and only if x
and y refer to the same object (x == y has the value true).
Without overriding the equals() method, the objects are different
Hence
System.out.println(cat1.equals(cat2)); // is false
That is because the == compare references and java.lang.Object.equals() translates to this==o thus return same as == in your case
In the case above you are using new operator to create two different objects hence both return false.
If you want .equals() to work as you are expecting, then override theequals() in your Cat class.
Ok, I have heard from many places and sources that whenever I override the equals() method, I need to override the hashCode() method as well. But consider the following piece of code
package test;
public class MyCustomObject {
int intVal1;
int intVal2;
public MyCustomObject(int val1, int val2){
intVal1 = val1;
intVal2 = val2;
}
public boolean equals(Object obj){
return (((MyCustomObject)obj).intVal1 == this.intVal1) &&
(((MyCustomObject)obj).intVal2 == this.intVal2);
}
public static void main(String a[]){
MyCustomObject m1 = new MyCustomObject(3,5);
MyCustomObject m2 = new MyCustomObject(3,5);
MyCustomObject m3 = new MyCustomObject(4,5);
System.out.println(m1.equals(m2));
System.out.println(m1.equals(m3));
}
}
Here the output is true, false exactly the way I want it to be and I dont care of overriding the hashCode() method at all. This means that hashCode() overriding is an option rather being a mandatory one as everyone says.
I want a second confirmation.
It works for you because your code does not use any functionality (HashMap, HashTable) which needs the hashCode() API.
However, you don't know whether your class (presumably not written as a one-off) will be later called in a code that does indeed use its objects as hash key, in which case things will be affected.
As per the documentation for Object class:
The general contract of hashCode is:
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. This integer need not remain consistent from one execution of an application to another execution of the same application.
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.
Because HashMap/Hashtable will lookup object by hashCode() first.
If they are not the same, hashmap will assert object are not the same and return not exists in the map.
The reason why you need to #Override neither or both, is because of the way they interrelate with the rest of the API.
You'll find that if you put m1 into a HashSet<MyCustomObject>, then it doesn't contains(m2). This is inconsistent behavior and can cause a lot of bugs and chaos.
The Java library has tons of functionalities. In order to make them work for you, you need to play by the rules, and making sure that equals and hashCode are consistent is one of the most important ones.
Most of the other comments already gave you the answer: you need to do it because there are collections (ie: HashSet, HashMap) that uses hashCode as an optimization to "index" object instances, an those optimizations expects that if: a.equals(b) ==> a.hashCode() == b.hashCode() (NOTE that the inverse doesn't hold).
But as an additional information you can do this exercise:
class Box {
private String value;
/* some boring setters and getters for value */
public int hashCode() { return value.hashCode(); }
public boolean equals(Object obj) {
if (obj != null && getClass().equals(obj.getClass()) {
return ((Box) obj).value.equals(value);
} else { return false; }
}
}
The do this:
Set<Box> s = new HashSet<Box>();
Box b = new Box();
b.setValue("hello");
s.add(b);
s.contains(b); // TRUE
b.setValue("other");
s.contains(b); // FALSE
s.iterator().next() == b // TRUE!!! b is in s but contains(b) returns false
What you learn from this example is that implementing equals or hashCode with properties that can be changed (mutable) is a really bad idea.
It is primarily important when searching for an object using its hashCode() value in a collection (i.e. HashMap, HashSet, etc.). Each object returns a different hashCode() value therefore you must override this method to consistently generate a hashCode value based on the state of the object to help the Collections algorithm locate values on the hash table.