//Written by K@stackoverflow
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K@stackoverflow
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output:
Answer from Kim on Stack Overflowrun:
-- Subash Adhikari - VS - K false
-- Subash Adhikari - VS - StackOverflow false
-- Subash Adhikari - VS - Subash Adhikari true
-- K - VS - StackOverflow false
-- K - VS - Subash Adhikari false
-- StackOverflow - VS - Subash Adhikari false
-- BUILD SUCCESSFUL (total time: 0 seconds)
//Written by K@stackoverflow
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K@stackoverflow
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output:
run:
-- Subash Adhikari - VS - K false
-- Subash Adhikari - VS - StackOverflow false
-- Subash Adhikari - VS - Subash Adhikari true
-- K - VS - StackOverflow false
-- K - VS - Subash Adhikari false
-- StackOverflow - VS - Subash Adhikari false
-- BUILD SUCCESSFUL (total time: 0 seconds)
Overloading vs Overriding
Introducing a new method signature that changes the parameter types is called overloading:
public boolean equals(People other){
Here People is different than Object.
When a method signature remains the identical to that of its superclass, it is called overriding and the @Override annotation helps distinguish the two at compile-time:
@Override
public boolean equals(Object other){
Without seeing the actual declaration of age, it is difficult to say why the error appears.
Override equals() When Comparing Objects - Why is Java Designed This Way?
java - Do we always need to override equals/hashcode when creating a new class? - Software Engineering Stack Exchange
object oriented - Overriding equals() method in Java - Software Engineering Stack Exchange
How to correctly override equals() method in Java? - TestMu AI Community
Videos
So, as a beginner, I found that comparing objects with the default equals() method actually does almost exactly the same thing like comparing them with ==. In other words, if two variables reference the same object, == will resolve as true and also equals() will resolve as true. But if we have two variables referencing two objects with a different identity but semantically the same (like, hmmm they look same to me! Same variables, etc.!) we actually have to come with our own equals() method and override the Java default method. (And when doing that, why not also override HashCode(), you know).
Now my question is not the typical "why???"(technicaly why it is needed?) but "why???" (why on earth was Java language designed this way)? Why Java does not have e.g. objectsEquals() method that would work for objects comparing all the fields etc. by default?
Why it even has equals() method that works the same as operator ==? In such a universe we could also have methods isTheSame(), isCompletelyTheSame() and thisIsEqual() all doing the same stuff? And also why we are even overriding it? Is it like "Java authors designed this method poorly or too generally, so let us made our better method that does also this and that". Why not create a framework class or our own class or just completely new method like sameObjects() and just use it with our own new beatiful name? Why overriding? Sorry, I am pretending rant, but I am just trying to be a little funny and also as a beginner I do not really understand this equals() override. What I am really trying to say is, I like to understand things deeply, in this case not only, it works basically in this and that way why but why it even exists as a part of our world:-)
should we always override the equals and hashCode even if we don’t intent at that point to use the class with any Collection classes?
No and I would go even further as to say you probably don't need to override them even if you are going to put them into a collection. The default implementations are perfectly compatible with collections as is. For lists, it's irrelevant anyway.
You should override equals if and only if your objects have a logical identity that is independent of their physical identity. In other words, if you want to have multiple objects that represent the same entity. Integer, is a good example of this. The integer 2 is always 2 regardless of whether there are 100 instances or 1 of the Integer object with the value of 2.
You must override hashcode if you've overridden equals.
That's it. There are no other good reasons to modify these.
As a side note, the use of equals to implement algorithms is highly overused. I would only do this if your object has a true logical identity. In most cases, whether two objects represent the same thing is highly context dependent and it's easier and better to use a property of the object explicitly and leave equals alone. There are many pitfalls to overriding equals, especially if you are using inheritance.
An example:
Let's say you have an online store and you are writing a component that fulfills orders. You decide to override the equals() method of the Order object to be based on the order number. As far as you know, that's the it's identity representation. You query a DB every so often and create objects from the response. You take each Order object and keep it as a key in a set which is all the orders in process. But there's a problem, orders can be modified in a certain time frame. You now might get a response from the DB that contains the same Order number but with different properties.
You can't just write this object to your set because you'll lose track of what was in process. You might end up processing the same order twice. So, what are the options? You could update the equals() method to include a version number or something additional. But now you have to go through your code to figure out where you need to base the logic on the having the same order number and where you need it to be based on the new object identity. In other words, there's not just one answer to whether two objects represent the same thing. In some contexts it's the order, and in some contexts it's the order and the version.
Instead of the set, you should build a map and use the order number as the key. Now you can check for the existence of the key in the map and retrieve the other object for comparison. This is much more straightforward and easy to understand than trying to make sense of when the equals method works they way you need it to for different needs.
A good example of this kind of complexity can be found in the BigDecimal class. You might think that BigDecimal("2.0") and BigDecimal("2.00") are equal. But they are not.
Conclusion:
It can make sense to implement equals() (make sure you update hashcode() too if you do) and doing so doesn't prevent the use of any of the techniques I describe here.
But if you are doing this:
- Make sure your object is immutable
- Use every meaningful property of the object to define equals.
- If you are using inheritance, either:
- make equals final on the base class OR
- you must check that the actual type matches in subclasses
If any of these aren't followed, you are in for trouble.
In Java, if you don’t override anything, equality is defined as object references being equal, and the hash code is the bit pattern of the reference. That works just fine to put objects into containers.
And there are objects where this is exactly right. For example an object representing a window in the UI. Two windows are equal if and only if they are the same window.
PS. It makes absolutely sense to have a set of windows, or a dictionary with windows as keys, or an array of windows and lookup if the array contains some window - so you need to be able to compare windows for equality.
equals() is a byproduct of an attempt to improve C++ when it was created. C++ has operator overloading which allows you to perform custom operations when called with otherwise valid operators such as <, >, !=, ==, and even =.
The team made the decision (wisely so) to make equality be class method rather than having external static methods as it was done in C++. However, this also meant that equals() coupled with hashCode() defined how such classes were handled in collection classes.
Since any class could in theory override equals() or hashCode(), it means that just because you have a collection of a certain type does not guarantee that behavior is uniform.
For instance, suppose class A has two members x and y used to determine equality. Along comes class B which has x, y, and z. If an instance of B had the same values x and y, how would you go about inserting this instance in a Set? If you call the equals of an instance of A, it will determine the two to be equal and if you call the equals of an instance of B, it will return false since it is not an instance of B.
To be perfectly correct, class B would have to treat member z as an additional condition only in the case in which it is an instance of B, otherwise it lends itself to the equals() method of class A, and, if such a thing is not possible, class B should not allow itself to override equals() or hashCode(). This creates a sticky situation since in theory you should not concern yourself with how the parent class works from an implementation standpoint (if done right anyway), but yet here we are.
You could make class A final to prevent such things from happening, but then of course you can never extend class A. Java makes a point of making certain standard classes like String final to prevent complications of this nature (very smart decision on their part). I think at the end of the day, what matters is that you are very careful in your usage of equals() and hashCode(). I try to use it sparingly, and I am always mindful of which classes I mean to be available in a library and which classes are for internal use as to not to create conditions where things could go horribly wrong.
The Liskov substitution principle is fine in theory, but in practice you can never quite manage it. Take Collection as an example. Collection is implemented by ArrayList, Set, or LinkedList among others. While it is true that you could achieve the same ultimate goal by replacing a Collection with say a HashSet, it is not an ideal implementation for performing operations on all objects contained within (better LinkedHashSet at that point). It wouldn't break existing code, but you may potentially render it grossly inefficient depending on how that Collection is used. Consider that this is a rather clean example too.
If you're lucky, only the implementation details change, but many behave radically different, with some methods throwing a NotImplementedException.
Thus requiring that classes implementing equals() must respect the Liskov substitution principle is asking a lot, and I suspect that they didn't want to alienate the majority of C++ programmers getting familiar with Java.
Simple argument against your logic is, what is the use of the equals method if it's modified to be a final. Then it would execute same logic against any given objects. If you want to compare A and AX in the object class what would be the logic?
Overriding of final method is allowed to be able to verify if two different objects (living in 2 different memory locations) are actually equals (== method is there to check if two references are of the same object) based on defined logic. For example if you have a user object like this,
User
{
string name;
string email;
string phone;
}
For one system, it would be the email you might interest to uniquely identifying a user. So you may compare based on email value. But some other system might use phone as the unique identifier. So they may decide to implement different equals methods.
When it comes to logic of A,AX and Liksov substitution principle which is,
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
So if you do,
AX ax1 = new AX();
AX ax2 = new AX();
ax1.equals(ax2);
and
A a = new AX();
AX ax = new AX();
a.equals(ax);
should give you the same output, and it will.