在Java中对子类进行排序

假设一个Superclass implements Comparable ,这样就是Arrays.sort(ArrayOfSuperInstances); 使用此compareTo(Superclass other)方法进行排序。 这是否保证Subclass extends Superclass的实例数组将使用Arrays.sort(ArrayOfSubInstances);以相同的方式Arrays.sort(ArrayOfSubInstances); ? (假设compareTo在子类定义中没有重载)

或者换句话说,默认情况下Subclass会inheritance其SuperclasscompareTo方法,以便可以盲目地使用Arrays.sort()知道它们将被排序为超类吗?

是的 – 这是多态性背后的整个原则,特别是Liskov替代原则 。 基本上,如果A是B的子类,那么你应该能够在任何你能够使用B的地方使用A,并且它应该基本上与B的任何其他实例(或B的其他子类)相同。

所以,它不仅会发生,而且几乎总是你想要发生的事情。 在子类中compareTo通常是错误的。

为什么? 那么, Comparable合同的一部分是比较是可传递的。 由于你的超类可能不知道它的子类正在做什么,如果一个子类以这样的方式覆盖compareTo ,它给出的答案不同于它的超类,它就会破坏契约。

例如,假设你有像Square和ColorSquare这样的东西。 Square的比较要比较两个方块的大小:

 @Override public int compareTo(Square other) { return this.len - other.len; } 

…而ColorSquare也增加了颜色的比较(让我们假设颜色是可比较的)。 Java不会让你拥有ColorSquare实现Comparable (因为它的超类已经实现了Comparable ),但你可以使用reflection来解决这个问题:

 @Override public int compareTo(Square other) { int cmp = super.compareTo(other); // don't do this! if (cmp == 0 && (other instanceof ColorSquare)) { ColorSquare otherColor = (ColorSquare) other; cmp = color.compareTo(otherColor.color); } return cmp; } 

这起初看起来很无辜。 如果两个形状都是ColorSquare,它们将在长度和颜色上进行比较; 否则,他们只会比较长度。

但是,如果你有:

 Square a = ... ColorSquare b = ... ColorSquare c = ... assert a.compareTo(b) == 0; // assume this and the other asserts succeed assert a.compareTo(c) == 0; // transitivity implies that b.compareTo(c) is also 0, but maybe // they have the same lengths but different color! assert b.compareTo(c) == 1; // contract is broken! 

默认情况下,如果您没有覆盖compareTo ,则将使用其超类的实现。


现在来做最佳实践 ,并打开Effective Java TM

compareTo的基本契约(和equals方法相同)是:

  1. 反身性 : x.compareTo(x)应始终为0,
  2. 对称性: x.compareTo(y)y.compareTo(x)应始终返回相反的符号或0,
  3. 传递性:如果x.compareTo(y) > 0y.compareTo(z) > 0 ; 然后x.compareTo(z) > 0

…除非您愿意放弃面向对象抽象的好处,否则无法在保留compareTo契约的情况下使用新值组件扩展可实例化类

更新 (感谢yshavit )

所以,你必须知道你在处理什么。 理想情况下,我也会在子类中编写compareTo ,它可能会也可能不会使用superClass的compareTo (对不起我创造的混乱)

在子类中实现compareToequals ,您将破坏对称性传递性 。 参考项目:7,Effective Java TM 。 它表明没有办法扩展一个类并覆盖equals方法(与compareTo具有相同的契约),以不同的方式:

在保留equals合同的同时,根本无法扩展可实例化的类并添加方面。 但是,有一个很好的解决方法。 遵循第14项“赞成组合而非inheritance。”的建议。而不是使ColorPoint扩展Point,为ColorPoint提供私有Point字段和公共视图方法(Item 4)

它应该,除非你的compareTo被覆盖或者调用一些被覆盖的方法。 一个更好的问题将显示你尝试时发生了什么,以及你关注的是什么。

是的,行为将与您没有覆盖compareTo方法相同,但是请确保您的子类不会以超级类中实现的compareTo()方法中断代码的方式更改任何属性