Use instanceof without knowing the type
Asked Answered
A

3

11

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?

Android answered 8/2, 2016 at 22:29 Comment(0)
M
9

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;
Machmeter answered 8/2, 2016 at 22:35 Comment(2)
For this application, the second method which requires the same class seems most appropriate.Smacker
or if( this.getClass()!=that.getClass() ) ... :)Leilani
H
1

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.

Hemi answered 8/2, 2016 at 22:39 Comment(5)
Would there be cases where getEntityClass() returns something other than getClass()? How do you see that being used?Smacker
Here is the weakness, You need to be carefull when you override the function. I can't put the code in the interface, or the calss would be the interface itself, so... But ENTITY generic type restrict types that inherit from ComparablebyIdHemi
You can't override getClass(), so if your default equals() implementation used getClass() rather than getEntityClass(), wouldn't it yield the same results?Smacker
I'm not sure i understand your question, i tried to factorise the equals method in the interface. So you no longer need to define `equals method, so... i think i don't understand you questionHemi
Hmm, it looks like a default method can't override a method from Object. Could you get this to compile?Smacker
P
1

I like rgetmann's answer https://mcmap.net/q/1000055/-use-instanceof-without-knowing-the-type 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;
 }
Plumbaginaceous answered 8/2, 2016 at 23:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.