Использовать instanceof без знания типа

Мои классы Java представляют собой сущности внутри базы данных, и я считаю целесообразным переопределить метод equals моих классов, чтобы сделать сравнения по id. Так, например, в моем классе Transaction у меня есть этот фрагмент кода

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

Теперь мне кажется немного уродливым, что каждый класс содержит один и тот же фрагмент кода, только с измененным именем класса. Я думал о том, что мои классы расширяют суперкласс MyEntity, где я бы написал вышеописанный метод, заменив instanceof Transaction на что-то вроде instanceof this.getClass(), но это не представляется возможным. Я также подумал о замене его на instanceof MyEntity, но это означает, что два объекта можно считать равными, даже если они принадлежат к разным классам, если они имеют одинаковый идентификатор. Есть ли другой способ?

Ответ 1

Вы можете использовать динамическую версию оператора instanceof, который является Class isInstance.

Определяет, является ли указанный объект совместимым с объектом с объектом, представленным этим классом.

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

Это не помешает экземпляру подкласса тестировать equals на объекте суперкласса, но динамический способ убедиться, что он точно такой же класс будет сравнивать два объекта Class для равенства.

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

Ответ 2

У вас может быть суперкласс с методом equals.

// 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());
    }

}

И тогда вы можете использовать его во всех своих классах следующим образом:

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

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

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

Преимущества:
 + Вы избегаете дублирования кода в каждом классе.
 + Все типы поддерживаются.
 + Больше никаких бросков.

Минусы:
 - Вам нужно определить метод getId() и getEntityClass() для каждого из ваших классов.

Ответ 3

Мне нравится rgetmann answer fooobar.com/info/556427/..., но он неполный. Я думаю, что приведенный ниже код (никоим образом не протестированный) не завершает его.

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