在Java中对子类进行排序

在Java中对子类进行排序,java,sorting,inheritance,Java,Sorting,Inheritance,假设一个超类实现了Comparable,这样Arrays.sort(ArrayOfSuperInstances)使用此比较(超类其他)方法进行排序。这是否保证子类扩展超类的实例数组将使用Arrays.sort(ArrayOfSubInstances)以相同的方式进行排序?(假设compareTo在子类定义中没有重载) 或者换句话说,默认情况下,子类是否会继承其超类的compareTo方法,这样人们就可以盲目地使用数组。sort()知道它们将被排序为超类,这是应该的,除非您的compareTo被重

假设一个
超类实现了Comparable
,这样
Arrays.sort(ArrayOfSuperInstances)
使用此
比较(超类其他)
方法进行排序。这是否保证
子类扩展超类的实例数组将使用
Arrays.sort(ArrayOfSubInstances)以相同的方式进行排序?(假设compareTo在子类定义中没有重载)


或者换句话说,默认情况下,
子类
是否会继承其
超类
compareTo
方法,这样人们就可以盲目地使用
数组。sort()
知道它们将被排序为超类,这是应该的,除非您的
compareTo
被重写,或者if调用了某个被重写的方法。一个更好的问题将显示您尝试时发生了什么,以及您关心的是什么。

默认情况下,如果您没有将
compareTo覆盖到
,将使用它的超类中的实现


现在来看看最佳实践,打开
effectivejava
TM

compareTo
(同样适用于
equals
方法)的基本契约是:

  • x.compareTo(x)
    应始终为0
  • 对称性:
    x.compareTo(y)
    y.compareTo(x)
    应始终返回相反的符号或0
  • 及物性:如果
    x.compareTo(y)>0
    y.compareTo(z)>0
    ;然后
    x.比较(z)>0
  • …除非您愿意放弃面向对象抽象的好处,否则在保留compareTo契约的同时,无法使用新的值组件扩展可实例化类

    更新(感谢)

    所以,你必须知道你在处理什么。理想情况下,我也会在子类中编写一个
    compareTo
    ,它可能会也可能不会使用超类的
    compareTo
    。(很抱歉我造成了混乱)

    在子类中实现
    compareTo
    equals
    ,将破坏对称性或传递性。参考第7项,有效JavaTM。它表明,没有办法扩展类并覆盖
    equals
    方法(它与
    compareTo
    具有相同的契约),以实现不同的功能:

    在保留equals契约的同时,根本没有办法扩展一个可实例化类并添加一个方面。然而,有一个很好的解决办法。遵循项目14的建议, “喜欢构图而不是继承”而不是使用色点扩展点, 为ColorPoint提供一个私有点域和一个公共视图方法(第4项)


    是的,行为与不重写compareTo方法相同,但请确保子类不会以可能破坏超级类中实现的compareTo()方法的代码的方式更改任何属性。是的,这是多形性背后的全部原理,特别是。基本上,如果A是B的一个子类,那么您应该能够在任何可以使用B的地方使用A,并且它的作用应该与B的任何其他实例(或B的其他子类)基本相同

    所以,它不仅会发生,而且几乎总是你想发生的。在子类中将
    进行比较通常是错误的

    为什么??嗯,
    可比的
    合同的一部分是比较是可传递的。由于您的超类可能不知道它的子类在做什么,如果子类重写
    compareTo
    ,从而给出与它的超类不同的答案,那么它就违反了约定

    例如,假设你有一个正方形和一个彩色正方形。正方形的
    compare to
    比较两个正方形的大小:

    @Override
    public int compareTo(Square other) {
        return this.len - other.len;
    }
    
    …而ColorSquare还为颜色添加了比较(假设颜色是可比较的)。Java不允许您使用ColorSquare实现
    Comparable
    (因为它的超类已经实现了
    Comparable
    ),但是您可以使用反射来解决这个问题:

    @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;
    }
    
    一开始这看起来很无辜。如果两种形状都是彩色正方形,它们将在长度和颜色上进行比较;否则,它们只会在长度上进行比较

    但如果你有:

    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!
    

    你不是说超类,不是子类吗?@user439407啊,我很着急。。。只是去研究有效的Java。感谢您指出。子类中的
    相比会做什么?它只能将另一个对象视为超类的对象,否则它将破坏传递性。出于同样的原因,它只能使用超类可用的
    this.*
    字段和方法。那么,它的实现与超级类的实现有何不同呢?感谢您的确认,我理解了Liskov替换原则(虽然我不知道这个术语),但我感到不安,因为Java中可比项的特殊作用。。。(也就是说,我不确定子类是否仍然可以作为超类进行比较,比较子类是否真的会使用超类
    comparable
    接口)。这个LSP在整个Java包层次结构中都是遵循的吗?还是有一些例外?@宣传比较虽然非常有用,但就语言而言并没有什么特别之处——它们与其他任何接口一样,通常的规则适用于动态解析。因此,如果调用
    o.compareTo(…)
    ,并且
    o
    属于子类,其中超类也实现了
    compareTo
    ,则将调用子类版本。如果您想比较子类的类型(例如,一堆彩色方块),最好编写一个自定义的
    比较器
    。至于JDK类中的LSP,我想不出有哪次它被违反了,但这是可能的。我想Java比我最初想象的更加一致!(在我的情况下,我确实需要超类compareTo,因此无需专门化比较。),