我正在开展一个项目,其中许多类需要equals
和hashCode
的适当典型实现:每个类都有一组在构造时初始化的最终字段" deep&# 34;不可变对象(在某些情况下可以接受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
}
这基本上是提出here提出的一些不同之处。
在我看来,这是一种简单而又好的方法,可以减少冗长,并且仍然可以有效地实现equals
和hashCode
。但是,由于我不记得曾经见过类似的东西(除了上面的答案),我想特别询问是否有一些观点反对这种方法(或者可能有一些改进可以应用),在将其应用于整个项目之前。
答案 0 :(得分:5)
我已经看到这种方法存在两个问题:
extend
制作课程AbstractHashable
,您不能再extend
来自任何其他课程。重新使用equals
和hashCode
第一个问题可以通过将更多元数据传递到AbstractHashable
类来解决,该类允许它识别哪些字段是可选的。例如,您可以将另一个数组传递给AbstractHashTable
,其中包含要通过setter忽略的索引位置作为其元素。第二个问题可以通过使用 Composition 来解决。而不是扩展AbstractHashTable
,重构它以便它可以与其用户建立 HAS-A 关系,而不是 IS-A 关系。
然而,由于我不记得曾经见过类似的东西(除了上面连接的答案),我想特别询问是否有一些反对这种方法的观点
这种方法肯定会影响代码的可读性方面。如果你能提出一种更具可读性的方法(比如使用注释),我就不会发现想要重用equals
和hashCode
实现有什么问题。
总而言之,像eclipse这样的现代IDE很容易为你生成equals
和hashCode
实现,所以真的需要提出这种解决方案吗?我相信没有。
答案 1 :(得分:0)
根据我的经验,这似乎很糟糕,因为你会增加你在运行时和编译时可以得到的错误(记住在列表等中使用这些对象现在可以给你带来惊喜的行为)。如果类中的字段顺序不同怎么办?其次你滥用继承吗?也许选择一些框架(https://projectlombok.org/)生成哈希码并基于注释等于?
答案 2 :(得分:0)
解决方案应该有效。
然而,从OOP的角度来看,感觉有点弱:
现代IDE很容易生成这样的方法,如果你想要你也可以使用框架(如Lombok)或库(如Guava&#39;对象/ Java 8&#39;对象)来简化这些方法。
答案 3 :(得分:0)
我建议您查看apache的HashCodeBuilder和EqualsBuilder类。它还有基于反射的方法,如reflectionHashCode和reflectionEquals。