-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Widespread use of bogus equals() Implementation #12040
Comments
Are you sure that is the problem? The If the check wasn't there, you could not do basically any kind of comparison, because you could not be sure of the type of the object being compared to. |
I don't see the problem as well. Can you please provide a unit test? This also might be helpfull. |
The check needs to be if (getClass() == other.getClass()), not instanceof. Otherwise, you will have but at the same time
This violates the symmetry requirement of the equals API doc |
Thanks for the reference, it proves my point. |
I assume you mean this case? class B {}
class A extends B {}
A a = new A();
B b = new B();
...
(a.equals(b)!=(b.equals(a)); |
yes |
Ok, so let's try to dissect this a bit, because I think we're starting to talk about at least 2 different things here. Let's expand on @dkuleshov 's example a little to conform more to the situation of class ResourceNode {
boolean equals(Object o) {
if (this == o) return true; <1>
if (!(o instanceof ResourceNode)) return false; <2>
... <3>
}
}
class ContainerNode extends ResourceNode {
} Now, let's have: ResourceNode r = new ResourceNode();
ContainerNode c = new ContainerNode(); Now, let's "debug"
Ok, now let's do the same with
Notice that in both cases, we got to the same point in execution, so the In Java, it is generally admissible to inherit Now if we observe that In the current case, 2 different subtypes of So in another words, If we went with your proposal, we'd violate that logic and would basically tightly couple a resource type to a node type (which might or might not be advisable, that's beyond my knowledge of the codebase). |
@tsmaeder it would be interesting if you could provide a failing unit test. It would be particularly useful to identify other classes affected by the same problem. |
@metlos you are absolutely correct, the idea that you described was my first thought as well. However after some meditation I came to a conclusion that the reporter has something different in his mind. The problem and confusion probably is that the example that is used is not really the most appropriate one. As I see, However my guess is that the idea of the issue is probably to point out that in cases where import org.junit.jupiter.api.Test;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
class EqualsTest {
@Test
void equalsShouldSymmetricForInheritance() {
X x = new X(1);
Y y = new Y(1);
assertEquals(x.equals(y), y.equals(x)); // will fail
}
}
class X {
int a;
X(int a) {
this.a = a;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof X)) {
return false;
}
X that = (X) o;
return Objects.equals(a, that.a);
}
}
class Y extends X {
Y(int a) {
super(a);
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Y)){
return false;
}
Y that = (Y) o;
return Objects.equals(a, that.a);
}
}
|
Actually, symmetry is given with the implementation in question, so I am wrong on that one 🤦♂️, but the case that remains is that folder.equals(package), which IMO only makes sense in very particular cases. In the case of folder vs. package node, it's clearly not what the artist intended. |
@metlos you're right. It's still bogus, though. |
I am closing this one as I am not sure if there is an action to take here. @tsmaeder reopen if you feel we still need to act on it |
In many places, we override
equals()
in a way that is simply wrong. Here's a snippet from ResourceNode.java:This code is not a correct because it does not correctly implement the contract for equals(), in particular it is not symmetric. For example,
ContainerNode
extendsResourceNode
, so you can have cases where resNode.equals(folderNode), but not folderNode.equals(resNode).This leads to all kinds of unexpected results, for example folder nodes not being correctly replaced with package nodes, in the project navigator because they are considered equal.
Depending on how collections are implemented, bogus equals implementations can lead to cases where we can have duplicate entries in sets or entries in maps not being found after being added with the same key.
The text was updated successfully, but these errors were encountered: