为了知道哪个Item 逻辑上等于其他Item 用于折叠,我将把责任放在Item 类本身上。您可以覆盖 equals 方法,但如果您要将它们放在 Set 某处,这可能会导致不良结果,因此最好使用单独的方法进行检查。
另一种选择是获取将用于此检查的那些字段并将它们转换为Item 的内部类。然后,equals 和 hashCode 可以仅被内部类覆盖,其实例用作 Map 的键。
但是,这些都不会自动包含您以后添加的任何新字段。因此,由维护类的人来确保将需要包含在检查(或 equals/hashCode)中的任何内容添加到方法中。
我真正想到的接近这一点的唯一方法是使用反射。如果必须考虑的任何事情只放在内部类中,那将起作用。如果您必须将其直接保留在 Item 类上,那么定义注释(具有运行时保留)可能会很有用。进行检查的代码(或 equals/hashCode,如果使用)可以反映类并使用每个带注释的字段。
注释可能如下所示:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CollapseField {
}
然后这样使用:
class Item {
@CollapseField
String type;
@CollapseField
boolean flag;
int size;
...
}
然后使用它的代码需要检查哪些字段被注释并获取它们的值(这两个操作都使用反射)并检查与其他对象的相等性以确定哪些属于一起。由于这可能会对性能产生相当大的影响,因此对哈希码之类的内容使用缓存将是一个好主意。
最后,我不确定是否值得对使用的值进行硬编码,除非您要在大量类中使用它,否则字段的数量可能会变得非常大。
最后,这对 Java 来说可能看起来很奇怪,但也许使用属性模式而不是字段可能是有意义的。尽管您会失去一些类型安全性。 Steve Yegge 发表了一篇很长但很有趣的帖子:http://steve-yegge.blogspot.be/2008/10/universal-design-pattern.html
这几乎是我能想到的全部。据我所知,没有标准的方法。也许有人知道一些提供解决方案的便捷库。
编辑:这是一个示例,其中要用于键的字段被制成一个内部类,它实现了equals 和hashCode,因此它可以用作Map 的键:
import java.util.Objects;
public class Item {
int size;
final Key key;
public class Key {
String type;
boolean flag;
public Key(String type, boolean flag) {
this.type = type;
this.flag = flag;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.type);
hash = 89 * hash + (this.flag ? 1 : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Key other = (Key) obj;
if (!Objects.equals(this.type, other.type)) {
return false;
}
if (this.flag != other.flag) {
return false;
}
return true;
}
}
public Item(String type, boolean flag, int size) {
key = new Key(type, flag);
this.size = size;
}
public String getType() {
return key.type;
}
public void setType(String type) {
this.key.type = type;
}
public boolean isFlag() {
return key.flag;
}
public void setFlag(boolean flag) {
this.key.flag = flag;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public Key getKey() {
return key;
}
}
Item 级别的 getter 和 setter 将一些字段委托给 Key。请注意,如果您只通过Item,则可能不需要Key 中的getter 和setter,因为包含类可以直接访问这些字段。如果您需要添加必须是键的一部分的字段,请将其添加到Key。如果一定不能用于识别,直接添加到Item。 equals 和 hashCode 如果您必须更新它们,任何体面的 IDE 都可以轻松地自动生成它们。
请注意,如果您在某些进行反射或自省的框架中使用该类,则此解决方案可能会中断。根据处理方式,Key 中的字段最终可能会被视为Item 的属性(由于 getter/setter)。 JPA 或 EJB 容器之类的东西直接通过反射接近字段可能无法使用。