将不可访问的字段(以及哪些不是也不能从任何其他公共字段派生)包含在equals()
中
这样做有什么不良后果?
这在某处被描述为不良做法吗? (我假设哈希/等于合同是好的)
我觉得,这样equals
在哲学上是非常错误的。世界上没有观察到equals
这样的{{1}}。但这会导致真正的编程错误行为,哲学不在吗?
答案 0 :(得分:4)
我可以想到一个不好的后果 - 您的类的用户可能会比较两个所有可公开访问的属性相同的对象,并且他们不会理解为什么您的equals
方法返回false。
我假设(并且您的编辑确认它)您指的是没有公共访问权限的私有字段(即没有getter),因为通常所有属性应该始终是私有的,并且只能由setter和getters访问。
答案 1 :(得分:4)
它非常好,有时是必要的。您不应该比较的唯一字段是标记为transient
的字段。
当且仅当它们的序列化相等时,我断言两个实例是相等的。这就是为什么transient
很重要以及字段封装程度不重要的原因。
答案 2 :(得分:2)
没关系。如果您自己编写类,则用户仍然可以使用您可能添加的getter和setter方法来获取和设置字段。但是,如果添加getter和setter方法,请确保字段是安全的。
如果您没有编写getter和setter方法,则用户可能很难根据所有公共字段的值来确定对象不相等的原因。说完这个之后,人们可以使用调试器来遍历私有字段的所有值。
另一种可能性是创建一个特殊方法,返回哪些特定字段不相等。虽然这并不严格符合字段应该被任何公共方法"无法访问的要求,但返回动态创建的字符串,基本类型或对象(例如字段名称数组)将保持方法安全。
答案 3 :(得分:1)
equals()
?有些字段定义了对象的标识,并且有些字段定义了它的属性。手机的序列号是其身份的一部分,其颜色或电池充电百分比只是一个属性。
equals()
应该只依赖于定义身份的字段,这与字段是否可以从外部访问无关,它来自字段本身的语义。
所以问题应该是:是否有任何理由将客户的身份隐藏起来?如果答案似乎是“是”,那么下一个更基本的问题是:是否有任何理由首先确定对象?毕竟,有很多物品不需要具有超出琐碎的身份。
有几个可能出现的问题,这是一个例子。
public class Foo {
private Bar hidden;
private String accessible;
@Override
public boolean equals( Object ob ) {
return hidden.equals(((Foo)ob).hidden);
}
@Override
public int hashCode() {
//Let's not forget that we MUST override hashCode() if we override equals() :)
}
}
现在想象一个天真的客户希望将这些放入TreeSet
。这个类不是Comparable
,但没什么可担心的,我们可以为它写一个比较器:
public class FooComparator implements Comparator<Foo> {
public int compare( Foo first, Foo second ) {
return first.getAccessible().compareTo( second.getAccessible() );
}
}
完成工作,很棒!
不良客户无意中违反了TreeSet
的合同,因为提供的比较与equals()
不一致。
让我们假设尽管知道这一切,你仍然希望自己的equals()
取决于隐藏的字段。因为您希望将它们放在HashSet
中。
然后你可以这样做:
public class Foo {
private Bar hidden;
private String accessible;
private final Helper helper = new Helper();
Helper helper() {
return helper;
}
class Helper {
@Override
public boolean equals( Object ob ) {
// you can access "hidden" from here
}
@Override
public int hashCode() {
// you can access "hidden" from here
}
public Foo foo() {
return Foo.this;
}
}
}
您将Helper
个对象放在HashSet
而不是Foo
中。虽然Helper
的可见性是包私有的,但在大多数情况下仍然可以接受,因为此级别不是公共API的一部分。
答案 4 :(得分:-2)
您应该放入使对象唯一的字段。如果你可以修改这些字段的重要性较低。