约书亚·布洛赫(Joshua Bloch)在他的书《有效的Java》中写道,当派生类向检查中添加额外字段时,equals()
的契约会出现陷阱。通常情况下,这会破坏对称性,但布洛赫表示,“可以在不违反equals契约的情况下向抽象类的子类添加值组件”。
显然这是正确的,因为抽象类不可能有实例,所以没有对称性可以违反。但其他子类呢?我编写了这个示例,故意省略哈希代码实现和空检查,以保持代码简短:
public abstract class Vehicle {
private final String color;
public Vehicle(String color) {
this.color = color;
}
public String getColor() {
return color;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Vehicle)) return false;
Vehicle that = (Vehicle) o;
return color.equals(that.color);
}
}
public class Bicycle extends Vehicle {
public Bicycle(String color) {
super(color);
}
}
public class Car extends Vehicle {
private final String model;
public Car(String color, String model) {
super(color);
this.model = model;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Car)) return false;
Car that = (Car) o;
return getColor().equals(that.getColor()) && model.equals(that.model);
}
}
当我用相同的颜色字符串创建每个类的一个实例时,equals()
的对称性被破坏:
Bicycle bicycle = new Bicycle("blue");
Car car = new Car("blue", "Mercedes");
bicycle.equals(car) <- true
car.equals(bicycle) <- false
我不确定如何以最好的方式处理这个问题。在抽象类中将equals()
声明为抽象,以在子类中强制实现?但是,在抽象类中完全不声明equals()
也可以达到同样的效果。
equals()
的对称性主要被破坏了,因为自行车
类是一个子类,它依赖于超级类(车辆)
来实现它自己的相等性。如果您为每个子类定义equals()
方法,那么您就不会遇到这个问题。
下面是每个类的equals()
实现。(只添加了自行车
equals()
,其他实现相同,但简化了。)
public abstract class Vehicle {
....
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Vehicle)) return false;
Vehicle that = (Vehicle) o;
return color.equals(that.color);
}
}
public class Bicycle extends Vehicle {
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Bicycle)) return false;
Bicycle that = (Bicycle) o;
return super.getColor().equals(that.getColor());
}
}
public class Car extends Vehicle {
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Car)) return false;
if (!super.equals(o)) return false;
Car car = (Car) o;
return model.equals(car.model);
}
}
// This is main class for testing the above functionality.
class MainClass {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("blue");
Car car = new Car("blue", "Mercedes");
System.out.println(bicycle.equals(car)); -> false
System.out.println(car.equals(bicycle)); -> false
}
}
或者你应该使用对象。getClass()
而不是@FranzBecker建议的超级类实现中的instanceof
运算符。子类仍然可以使用instanceOf
操作符,而不会出现任何问题。
public abstract class Vehicle {
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if ((this.getClass() != o.getClass())) return false;
Vehicle that = (Vehicle) o;
return color.equals(that.color);
}
}
比较类对象而不是执行instanceof
检查可以解决问题。
if (getClass() != obj.getClass()) {
return false;
}
以下是完整的实现(由Eclipse生成):
public class Vehicle {
// ...
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Vehicle other = (Vehicle) obj;
if (color == null) {
if (other.color != null) {
return false;
}
} else if (!color.equals(other.color)) {
return false;
}
return true;
}
}
public class Car extends Vehicle {
// ...
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Car other = (Car) obj;
if (model == null) {
if (other.model != null) {
return false;
}
} else if (!model.equals(other.model)) {
return false;
}
return true;
}
}
然后,示例中的两个检查都将产生false
。
Java的equals契约在这种情况下变得特别不稳定,最终这一切都变成了程序员偏好和需求的问题。我记得我也遇到过同样的问题,我看到了这篇文章,在考虑Java的equals契约时,它讨论了几种可能性和问题。它最终基本上会说,如果不打破Java equals契约,就无法正确地完成它。
在处理抽象类时,我个人的偏好是根本不给抽象类一个equals方法。这没有道理。不能有两个抽象类型的对象;你应该如何比较它们?取而代之的是,我给每个子类赋予它自己的等号,并且每当调用equals()
时,运行时都会处理其余的。总的来说,我在这篇文章中最常遵循的解决方案是“只有同一类的对象才能被比较”,这对我来说似乎是最明智的。
我仍然试图掌握抽象基类的概念,以及从派生类中可以做什么和不能做什么。 我有以下代码: 基类中的示例- 在派生类中
我学会了通过存储基类指针将派生类指针存储在基类向量中: 但是如果我有一个抽象基类: 从中派生出另外两个抽象类。 以及来自二级抽象类的其他几个派生类: 是否有可能将它们全部存储在多态性载体中?和往常一样,我做了以下工作: 但是如何将两个多态向量存储在基类向量中呢?
是否有任何JAXB绑定可以告诉JAXB代码生成器将Java类生成为,而不必在XSD中将相应的XML类型标记为? 情况如下: > 我在xsd中定义架构: 我使用内联JAXB绑定(“inline”==“直接在模式中”)来指示应该生成JAXB类的包(): 我使用内联JAXB绑定为我的每个复杂类型(在本例中、和)指示实现类的名称: 我从模式生成JAXB类。这导致: 我自己编写类: 使用这两个类层次结构这样
问题内容: 有这样的事情: 抽象类: 和扩展器: 我想要的是扩展,因为如果不处理它,然后尝试在中处理它。例如add- 但同时让类处理默认值,例如。 这是完全错误的方法吗? 到目前为止,我所做的就是添加一个接口。通常: 在课堂上: 在课堂上: 这看起来像是可用的设计吗? 并且,主要问题: 有没有什么好办法扩展该类,以便可以使用与in 中相同的切换方法?我想知道的是,有没有一个更好的设计,第二个是是否
我们不能创建一个抽象类的对象,对吧?那么如何调用在抽象基类和派生类中都有定义的虚函数呢?我想在抽象基类中执行代码,但目前,我正在使用派生类的对象。 有什么方法可以使用对象t调用基类函数吗?如果不可能,我需要做什么来调用基类函数?
问题内容: 结果如下: 2011-09-24 14:10:51 -0400 2011年9月24日星期六20:10:51 为什么当我解析来自format()的日期时,它不遵守时区? 问题答案: 您正在打印调用的结果,该调用 始终 使用默认时区。基本上,除了调试之外,您不应该使用其他任何东西。 不要忘记,一个不 具有 时区-它代表着一个时刻,因为自Unix纪元(午夜1970年1月1日UTC)毫秒。 如