Java 在equals()中包含非公共字段是一种不好的做法吗
在Java 在equals()中包含非公共字段是一种不好的做法吗,java,Java,在equals()中包含任何公共方法都无法访问的字段(这些字段本身不是也不能从任何其他公共字段派生)是否是一种不良做法 这样做可能产生什么不良后果 这是否被描述为某个地方的不良做法? (我假设hash/equals契约是可以的) 我觉得这样的等于在哲学上是非常错误的。这种等于在世界上是不存在的。但是,撇开哲学不谈,它会导致真正的编程错误行为吗?我能想到一个坏的结果——类的用户可能会比较两个对象,其中所有公共可访问属性都是相等的,他们不会理解为什么equals方法返回false 我假设(您的编辑确
equals()
中包含任何公共方法都无法访问的字段(这些字段本身不是也不能从任何其他公共字段派生)是否是一种不良做法
这样做可能产生什么不良后果
这是否被描述为某个地方的不良做法?
(我假设hash/equals契约是可以的)
我觉得这样的等于在哲学上是非常错误的。这种等于在世界上是不存在的。但是,撇开哲学不谈,它会导致真正的编程错误行为吗?我能想到一个坏的结果——类的用户可能会比较两个对象,其中所有公共可访问属性都是相等的,他们不会理解为什么
equals
方法返回false
我假设(您的编辑确认了这一点)您指的是没有公共访问权限的私有字段(即没有getter),因为一般来说,所有属性都应该是私有的,并且只能由setter和getter访问。您应该放入使对象唯一的字段。如果可以修改,则字段的重要性较小。没关系。如果您自己编写类,用户仍然可以使用您可能添加的getter和setter方法获取和设置字段。但是,如果添加getter和setter方法,请确保字段是安全的 如果您没有编写getter和setter方法,用户可能很难根据所有公共字段的值确定对象不相等的原因。话虽如此,您可能可以使用调试器逐步遍历私有字段的所有值
另一种可能性是创建一个特殊的方法,该方法将返回不相等的特定字段。尽管这并不严格符合“任何公共方法都无法访问”字段的要求,但返回动态创建的字符串、基本类型或对象(如字段名数组)将确保方法的安全。这是绝对正确的,有时也是必要的。唯一不应该比较的字段是标记为
transient
的字段
我断言两个实例是相等的,当且仅当它们的序列化相等时。这就是为什么transient
很重要,而字段封装的程度并不重要。应该在equals()
中做什么?
有些字段定义对象的标识,有些字段定义对象的属性。手机的序列号是其身份的一部分,其颜色或电池电量百分比只是一种属性
equals()
应该只依赖于定义标识的字段,这与字段是否可以从外部访问无关,它来自字段本身的语义
所以问题应该是:有没有任何理由对其客户机隐藏对象的身份?如果答案似乎是“是”,那么下一个更基本的问题是:有没有理由首先识别这些物体?毕竟,有很多对象不需要在琐碎之外有一个身份
有几个可能的问题由此产生,这里有一个例子
问题实例
现在想象一下,一个天真的客户想要把这些放到树集中。该类没有可比性,但无需担心,我们可以为其编写一个比较器:
public class FooComparator implements Comparator<Foo> {
public int compare( Foo first, Foo second ) {
return first.getAccessible().compareTo( second.getAccessible() );
}
}
然后将Helper
对象放在HashSet
中,而不是Foo
。尽管Helper
s可见性是包私有的,但在大多数情况下仍然可以接受,因为此级别不是公共API的一部分。使用公共字段是不好的做法:)为什么不好?我不记得我上一次看到公共领域()是什么时候了。@Alex:所以在这种情况下,它太宽泛了,但我个人认为这样做的影响是否是负面的,这在很大程度上取决于情况too@Alex:如果有100个例子说明它如何产生负面影响呢?再说一次,谁来决定效果是否是负面的?@Alex:如果你不能证明,那么也许你有一个这样做的场景。例如,假设您有一个私有字段,它实际上存储了某种“元信息”(它可能是一些昂贵计算的缓存),不需要是公共的,但对于检查相等性很重要。。。再一次。。。这么多的可能性。也许你应该找一个聊天室来进行这样的讨论这有点误导。如果一个类的用户需要检查该类的两个实例是否相等,则应该使用equals
,而不是一些自制的属性比较方法。因为,OOP是关于状态和行为的,两个对象的相等性应该由状态决定,状态包括所有成员的值,公共的或私有的。@Krumia假设我编写代码:。。。字符串str=“foo”;字符串str2=“foo”;if(str.equals(str2)){…}else{…}…
如果执行else
子句,我会非常惊讶。@Eran:您会感到惊讶,因为字符串不是这样设计的。OP指的是定制类,它可能有许多不同的数据属性来确定相等性,并且并非所有的都需要公开。关键是,平等的规则是由规范决定的。。。哪一种可能anything@Eran:我也会的。如果您遇到OP描述的此问题,则必须满足以下条件之一:1。您尚未公开本应使用getter公开的成员。通过getter公开实例成员。2.您已将方法中本应为局部变量的内容提升为实例变量。将该实例成员降级为相关方法的局部变量。3.您必须使用实例成员,因为您不必要地拆分了一个方法。所以为了传递状态,您需要实例成员。这是退出
public class FooComparator implements Comparator<Foo> {
public int compare( Foo first, Foo second ) {
return first.getAccessible().compareTo( second.getAccessible() );
}
}
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;
}
}
}