在Java中对子类进行排序
假设一个Superclass implements Comparable
,这样就是Arrays.sort(ArrayOfSuperInstances);
使用此compareTo(Superclass other)
方法进行排序。 这是否保证Subclass extends Superclass
的实例数组将使用Arrays.sort(ArrayOfSubInstances);
以相同的方式Arrays.sort(ArrayOfSubInstances);
? (假设compareTo在子类定义中没有重载)
或者换句话说,默认情况下Subclass
会inheritance其Superclass
的compareTo
方法,以便可以盲目地使用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
方法相同)是:
- 反身性 :
x.compareTo(x)
应始终为0, - 对称性:
x.compareTo(y)
和y.compareTo(x)
应始终返回相反的符号或0, - 传递性:如果
x.compareTo(y) > 0
且y.compareTo(z) > 0
; 然后x.compareTo(z) > 0
和
…除非您愿意放弃面向对象抽象的好处,否则无法在保留compareTo契约的情况下使用新值组件扩展可实例化类
更新 (感谢yshavit )
所以,你必须知道你在处理什么。 理想情况下,我也会在子类中编写 (对不起我创造的混乱) compareTo
,它可能会也可能不会使用superClass的compareTo
。
在子类中实现compareTo
或equals
,您将破坏对称性或传递性 。 参考项目:7,Effective Java TM 。 它表明没有办法扩展一个类并覆盖equals
方法(与compareTo
具有相同的契约),以不同的方式:
在保留equals合同的同时,根本无法扩展可实例化的类并添加方面。 但是,有一个很好的解决方法。 遵循第14项“赞成组合而非inheritance。”的建议。而不是使ColorPoint扩展Point,为ColorPoint提供私有Point字段和公共视图方法(Item 4)
它应该,除非你的compareTo
被覆盖或者调用一些被覆盖的方法。 一个更好的问题将显示你尝试时发生了什么,以及你关注的是什么。
是的,行为将与您没有覆盖compareTo方法相同,但是请确保您的子类不会以超级类中实现的compareTo()方法中断代码的方式更改任何属性