How to implement “equals” method for generics using “instanceof”?

人走茶凉 提交于 2020-01-11 16:36:04

问题


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!


回答1:


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




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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;
    }


来源:https://stackoverflow.com/questions/16382887/how-to-implement-equals-method-for-generics-using-instanceof

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!