splinter123 February 2016

Use instanceof without knowing the type

My Java classes represent entities inside a database, and I find it practical to override the equals method of my classes to make comparisons by id. So for example in my Transaction class I have this piece of code

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof Transaction))return false;
    Transaction otherTrans = (Transaction) other;
    if (id == null || otherTrans.id == null) return false;
    return id.equals(otherTrans.id);
}

Now it seems a bit ugly to me that every class holds the same piece of code, with only the name of the class changed. I thought about making my classes extend a superclass MyEntity where I would write the above method, replacing instanceof Transaction with something like instanceof this.getClass(), but this doesn't seem to be possible. I also thought about replacing it with instanceof MyEntity, but that means two object could be considered equal even if they belonged to different classes, as long as they have the same id. Is there any other way?

Answers


rgettman February 2016

You can use the dynamic version of the instanceof operator, which is Class's isInstance method.

Determines if the specified Object is assignment-compatible with the object represented by this Class.

if (!(getClass().isInstance(other))) return false;

This will not prevent an instance of a subclass from testing equals on a superclass object, but a dynamic way of ensuring that it's the exact same class would be to compare the two Class objects for equality.

if (!(getClass().equals(other.getClass()))) return false;


Anthony Raymond February 2016

You can have an super class with an equals method.

// Where ENTITY would be the type of the class to compare, and ID the type of the id
public abstract class ComparableById<ENTITY extends ComparableById, ID> {

    protected abstract Class<?> getEntityClass();
    protected abstract ID getId();

    @Override
    public boolean equals(Object other) {
        if (other == null) return false;
        if (other == this) return true;
        if (!getEntityClass().isInstance(other)) return false;
        ComparableById o = (ComparableById) other;
        if (getId() == null || o.getId() == null) return false;
        return getId().equals(o.getId());
    }

}

And then you can use it in all of your class this way :

@Entity
public class TeacherEntity extends ComparablebyId<TeacherEntity, Long> {
    private Long id;

    @Override
    public Long getId() {
        return this.id;
    }

    @Override
    public getEntityClass() {
        return this.getClass();
    }
}

Benefits :
+ You avoid code duplication in each classes.
+ All types are suported.
+ No more casts.

Cons :
- You need to define getId() and getEntityClass() method for each of your class.


emory February 2016

I like rgetmann's answer http://stackoverflow.com/a/35280674/348975 but it is incomplete. I think the below code (not in any way tested) completes it.

 boolean equals(Object b){
     return getClass().equals(b.getClass())
              && id==getClass().cast(b).id;
 }

Post Status

Asked in February 2016
Viewed 3,807 times
Voted 5
Answered 3 times

Search




Leave an answer