繁体   English   中英

equals和hashCode的可重用实现

[英]Reusable implementation of equals and hashCode

我正在一个项目中,其中许多类需要equalshashCode适当典型实现:每个类具有一组最终字段,这些字段在构造时初始化为“深度”不可变的对象(在某些情况下,应接受null )用于哈希和比较。

为了减少样板代码的数量,我考虑过编写一个提供此类行为常见实现的抽象类。

public abstract class AbstractHashable {

    /** List of fields used for comparison. */
    private final Object[] fields;

    /** Precomputed hash. */
    private final int hash;

    /**
     * Constructor to be invoked by subclasses.
     * @param fields list of fields used for comparison between objects of this
     * class, they must be in constant number for each class
     */
    protected AbstractHashable(Object... fields) {
        this.fields = fields;
        hash = 31 * getClass().hashCode() + Objects.hash(fields);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null || !getClass().equals(obj.getClass())) {
            return false;
        }
        AbstractHashable other = (AbstractHashable) obj;
        if (fields.length != other.fields.length) {
            throw new UnsupportedOperationException(
                    "objects of same class must have the same number of fields");
        }
        for (int i=0; i<fields.length; i++) {
            if (!fields[i].equals(other.fields[i])) {
                return false;
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        return hash;
    }

}

打算这样使用:

public class SomeObject extends AbstractHashable {

    // both Foo and Bar have no mutable state
    private final Foo foo;
    private final Bar bar;

    public SomeObject(Foo foo, Bar bar) {
        super(foo, bar);
        this.foo = Objects.requireNonNull(foo);
        this.bar = bar; // null supported
    }

    // other methods, no equals or hashCode needed

}

这基本上是这里提出的内容,但有一些差异。

在我看来,这是一种减少冗长但仍有效的equalshashCode实现的简单而有效的方法。 但是,由于我不记得曾经见过类似的东西(上面链接中的答案除外),所以我想特别问一下在应用之前是否有反对这种方法的观点(或可能会有所改进)它贯穿整个项目。

我已经看到此方法有两个问题:

  1. 当且仅当它们的所有字段都匹配时,两个对象才被视为相等。 在现实世界中,情况并非总是如此。
  2. 通过使您的类从AbstractHashable extend ,您不再extend从任何其他类extend 重用equalshashCode付出了很高的代价。

通过将更多的元数据传递给AbstractHashable类可以解决第一个问题,该类允许它标识哪些字段是可选的。 例如,您可以将另一个数组传递给AbstractHashTable ,该数组包含通过setter作为其元素被忽略的索引位置。 第二个问题可以通过使用Composition解决。 对其进行重构,而不是扩展AbstractHashTable ,以便它可以与其用户建立HAS-A关系,而不是IS-A关系。

但是,由于我不记得曾经见过类似的东西(上面链接中的答案除外),所以我想特别问一下这种方法是否有针对性

这种方法肯定会影响代码的可读性。 如果您想出一种更具可读性的方法(例如通过使用批注),那么我想重用equalshashCode实现不会有任何问题。

综上所述,诸如eclipse之类的现代IDE可以轻松为您生成equalshashCode实现,因此,真的有必要提出这种解决方案吗? 我相信不会

根据我的经验,这似乎很糟糕,因为您会增加在运行时与编译时可能遇到的错误(记住,在列表等中对这些对象的所有使用。现在可以给您带来扩展的行为)。 如果类中字段的顺序不同怎么办? 其次,您滥用继承吗? Maybee选择某种框架( https://projectlombok.org/ ),该框架会基于注释生成哈希码和等号?

该解决方案应该起作用。

但是,从OOP的角度来看,它有点弱:

  • 您正在复制数据(超类中的字段与对象中的字段),因此您的对象大一倍; 另外,如果您需要使它们可变,则需要在两个地方维护状态
  • 类层次结构被强制,仅提供equals / hashCode

现代IDE很容易生成此类方法,如果您愿意,还可以使用框架(如Lombok)或库(如Guava的Objects / Java 8的Objects)来简化这些方法。

我建议看看apache的HashCodeBuilderEqualsBuilder类。 它也具有基于反射的方法,例如reflectionHashCode和reflectionEquals。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM