5

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

或者换句话说,Subclass默认情况下会继承它的compareTo方法Superclass,这样人们就可以盲目地使用Arrays.sort()知道它们将被排序为超类吗?

4

4 回答 4

7

是的——这就是多态性背后的全部原则,特别是Liskov 替换原则。基本上,如果 A 是 B 的子类,那么您应该能够在任何可以使用 B 的地方使用 A,并且它的行为本质上应该与 B 的任何其他实例(或 B 的其他子类)相同。

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

为什么?好吧,Comparable<T>合同的一部分是比较是传递的。由于你的超类可能不知道它的子类在做什么,如果一个子类compareTo以这样一种方式覆盖,它给出的答案与它的超类不同,它就会违反合同。

例如,假设您有类似 Square 和 ColorSquare 的东西。SquarecompareTo比较两个正方形的大小:

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

...而 ColorSquare 还添加了颜色比较(假设颜色是 Comparable)。Java 不会让你有 ColorSquare 实现Comparable<ColorSquare>(因为它的超类已经实现Comparable<Square>),但你可以使用反射来解决这个问题:

@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!
于 2012-08-13T06:05:04.097 回答
3

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


现在来最佳实践,打开Effective JavaTM

compareTo(方法也是如此)的基本合同equals是:

  1. 自反性x.compareTo(x)应始终为 0,
  2. 对称性:x.compareTo(y)并且y.compareTo(x)应该总是返回相反的符号或 0,
  3. 传递性: ifx.compareTo(y) > 0y.compareTo(z) > 0; 然后x.compareTo(z) > 0

...没有办法在保留 compareTo 合同的同时用新的值组件扩展可实例化类,除非您愿意放弃面向对象抽象的好处

更新(感谢yshavit

所以,你必须知道你在处理什么。理想情况下,我也会写一个compareToin 子类,它可能使用也可能不使用 superClass 的compareTo. (对我造成的混乱感到抱歉)

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

根本没有办法扩展一个可实例化的类并添加一个方面,同时保留 equals 合同。但是,有一个很好的解决方法。遵循第 14 条“优先组合优于继承”的建议。不要让 ColorPoint 扩展 Point,而是给 ColorPoint 一个私有 Point 字段和一个公共视图方法(第 4 项)

于 2012-08-13T05:41:24.670 回答
0

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

于 2012-08-13T05:40:58.353 回答
0

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

于 2012-08-13T05:42:00.987 回答