I have a class that accepts a generic type, and I want to override the equals
method in a non-awkward way (i.e. something that looks clean and has minimal amount of code, but for a very general use case).
Right now I have something like this:
public class SingularNode<T> {
private T value;
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object other){
if(other instanceof SingularNode<?>){
if(((SingularNode<T>)other).value.equals(value)){
return true;
}
}
return false;
}
}
Which, I'm guessing, is pretty flawed - I'm doing a cast to SingularNode<T>
on the other
object, which could potentially throw an error.
Another thing is - when I do if(other instanceof SingularNode<?>)
I'm actually not checking exactly the right thing. I actually want to check against type T
and not type ?
. Whenever I try to make the ?
into T
, I get some error like:
Cannot perform instanceof check against parameterized type SingularNode<T>
. Use the form SingularNode<?>
instead, since further generic type information will be erased at runtime
How can I get around this? Is there some way to do T.class.isInstance(other);
?
I suppose there's one really ugly hack solution like this:
@SuppressWarnings("unchecked")
public boolean isEqualTo(Class<?> c, Object obj){
if(c.isInstance(obj) && c.isInstance(this)){
if(((SingularNode<T>)obj).value.equals(value)){
return true;
}
}
return false;
}
But that just looks really awkward with the extra method parameter, and it's also not a built-in function like equals
is.
Any one who understand generics please explain this? I'm not that proficient with Java, as you can clearly see, so please explain with a tad bit more detail!
This version gives no warnings
public boolean equals(Object other){
if (other instanceof SingularNode<?>){
if ( ((SingularNode<?>)other).value.equals(value) ){
return true;
}
}
return false;
}
As for casting to SingularNode<T>
it does not help anything, you cannot assume that T
can be anything but Object
.
Learn more about how generics are compiled in Java at
https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
Evgeniy's solution and Michal's reasoning are correct - you don't need to worry about the type of T
here. The reason is that the equals
method doesn't depend on generics to work correctly. Instead, it is declared by Object
and it takes an Object
. Thus, it's responsible for checking the runtime type of whatever was passed in.
If this
happens to be SingularNode<String>
and you compare it with a SingularNode<Integer>
, then ((SingularNode<?>)other).value.equals(value)
is perfectly fine because calling Integer.equals
with a String
argument will correctly return false
.
I put answer here to put code..
In your example you have (in pseudo code) Integer(5).equals(Char('k'))
which is false
, according to following equals implementation on java.lang.Integer
:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
Going that way you don't have to worry about casting.
I have the same problem, however it is more general. I have a class where I do have 3 generic types. I do not need to store any variable of those types because this class is used for transformation. However there are stored 'request' variables and I do use cache based on this class, so I need to implement equals() method that is based on those generics.
Do you know if there is any approach how to do it without reflection? Maybe internal variable of that type.. However it's null then.
public class TheClass<I, O, M> {
private ClassA param1;
private ClassB param2;
private ClassC<M> param3;
private BiFunction<ClassC<M>, I, Optional<O>> mapping;
public ClassD<O, M> doSomething(ClassD<I, M> param) {
...
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
if (getClass() != o.getClass()) {
return false;
}
TheClass<?, ?, ?> that = (TheClass<?, ?, ?>) o;
return Objects.equals(getParam1(), that.getParam1()) &&
Objects.equals(getParam2(), that.getParam2()) &&
Objects.equals(getParam3(), that.getParam3());
}
}
For a better imagination... I have set of DAO objects getting data from database. On the other hand, we do have another set of API providers that provide similar data in different format (REST, internal systems..)
We need a mapping function from one type to another. We use caching for better performance and the only man-in-the-middle is this class.
You dont need to use any casting.
Best equals to implementation I see like this
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof VehicleModel)) return false;
VehicleModel that = (VehicleModel) o;
if (vehicleName != null ? !vehicleName.equals(that.vehicleName) : that.vehicleName != null)
return false;
return true;
}