[英]Reusable implementation of equals and hashCode
I'm working on a project where many classes need proper typical implementations of equals
and hashCode
: each class has a set of final fields initialized at construction with "deeply" immutable objects ( null
s are intended to be accepted in some cases) to be used for hashing and comparison. 我正在一个项目中,其中许多类需要
equals
和hashCode
适当典型实现:每个类具有一组最终字段,这些字段在构造时初始化为“深度”不可变的对象(在某些情况下,应接受null
)用于哈希和比较。
To reduce the amount of boilerplate code, I thought about writing an abstract class providing common implementations of such behavior. 为了减少样板代码的数量,我考虑过编写一个提供此类行为常见实现的抽象类。
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;
}
}
This is intended to be used like this: 打算这样使用:
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
}
This is basically what proposed here with some differences. 这基本上是这里提出的内容,但有一些差异。
This seems to me a straightforward yet good approach to reduce verbosity and still have efficient implementations of equals
and hashCode
. 在我看来,这是一种减少冗长但仍有效的
equals
和hashCode
实现的简单而有效的方法。 However, as I don't recall to have ever seen something similar (except in the answer linked above), I would like to specifically ask whether is there some point against this approach (or possibly some improvement which could be applied), before applying it across the whole project. 但是,由于我不记得曾经见过类似的东西(上面链接中的答案除外),所以我想特别问一下在应用之前是否有反对这种方法的观点(或可能会有所改进)它贯穿整个项目。
I see two issues with this approach already : 我已经看到此方法有两个问题:
extend
from AbstractHashable
, you can no longer extend
from any other classes. AbstractHashable
extend
,您不再extend
从任何其他类extend
。 This is quite a high price to be paying for reusing equals
and hashCode
. equals
和hashCode
付出了很高的代价。 The first issue could be solved by passing more meta data to your AbstractHashable
class that allows it to identify what fields are optional. 通过将更多的元数据传递给
AbstractHashable
类可以解决第一个问题,该类允许它标识哪些字段是可选的。 For instance, you could pass another array to AbstractHashTable
that contains index positions to be ignored as its elements via a setter. 例如,您可以将另一个数组传递给
AbstractHashTable
,该数组包含通过setter作为其元素被忽略的索引位置。 The second issue can be solved by using Composition . 第二个问题可以通过使用Composition解决。 Instead of extending
AbstractHashTable
, refactor it so that it can establish a HAS-A relationship with its users rather than an IS-A relationship. 对其进行重构,而不是扩展
AbstractHashTable
,以便它可以与其用户建立HAS-A关系,而不是IS-A关系。
However, as I don't recall to have ever seen something similar (except in the answer linked above), I would like to specifically ask whether is there some point against this approach
但是,由于我不记得曾经见过类似的东西(上面链接中的答案除外),所以我想特别问一下这种方法是否有针对性
This approach will definitely impact the readability aspect of your code. 这种方法肯定会影响代码的可读性。 If you can come up with a more readable approach (say by using annotations), I don't see anything wrong with wanting to reuse
equals
and hashCode
implementations. 如果您想出一种更具可读性的方法(例如通过使用批注),那么我想重用
equals
和hashCode
实现不会有任何问题。
That all being said, modern IDEs such as eclipse can easily generate the equals
and hashCode
implementation for you so is there really a need to come up with a solution of this sort? 综上所述,诸如eclipse之类的现代IDE可以轻松为您生成
equals
和hashCode
实现,因此,真的有必要提出这种解决方案吗? I believe no . 我相信不会 。
Based on my experience this seems bad as you will increase the error you can get in runtime vs. compile time (remember all use of these objects in lists etc. can now give you unexpexted behaviour). 根据我的经验,这似乎很糟糕,因为您会增加在运行时与编译时可能遇到的错误(记住,在列表等中对这些对象的所有使用。现在可以给您带来扩展的行为)。 What if the order of fields in the classes are different?
如果类中字段的顺序不同怎么办? Secondly are you misusing inheritance?
其次,您滥用继承吗? Maybee opt for some framework ( https://projectlombok.org/ ) which generate hashcode and equals based on annotation?
Maybee选择某种框架( https://projectlombok.org/ ),该框架会基于注释生成哈希码和等号?
The solution should work. 该解决方案应该起作用。
However it feels a bit weak from OOP point of view: 但是,从OOP的角度来看,它有点弱:
Modern IDEs generate such methods quite easily, if you want you can also use frameworks (like Lombok) or libraries (like Guava's Objects/Java 8's Objects) to simplify these methods. 现代IDE很容易生成此类方法,如果您愿意,还可以使用框架(如Lombok)或库(如Guava的Objects / Java 8的Objects)来简化这些方法。
I would suggest to have a look at apache's HashCodeBuilder and EqualsBuilder classes. 我建议看看apache的HashCodeBuilder和EqualsBuilder类。 It has reflection based methods like reflectionHashCode and reflectionEquals as well.
它也具有基于反射的方法,例如reflectionHashCode和reflectionEquals。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.