在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,因此无需专门化比较。),