重写子类中的compareTo方法时出现问题



我正在创建子类,其中有一些类元素。现在我想覆盖超类中的compareTo方法,并在新的compareTo-方法中使用子类中的变量作为参数,但我遇到了一个错误,说compareTo方法的参数必须与超类中相同。

这个问题有什么解决办法吗?

提前感谢!

我尝试过的解决方案是:

1( 只是简单地在子类中编写新方法(而不是覆盖超类中的方法(2( 在具有参数的超类中创建新的compareTo方法I want

这个程序是这样工作的,但我觉得这不是解决这个问题的正确方法

有人可以对此提出建议或建议吗?

我正在创建子类,其中有一些类元素。

好的。

现在我想覆盖superclass中的compareTo方法,并使用子类中的变量

不可能。

Comparable是一个"合同接口"——你不能只是实现这些方法;你不是";完成";当你实现了每个方法,并且它们在编译和运行时(看起来(没有抛出异常:文档中说明了你需要遵守的编译器无法检查的其他规则。编译器无法检查您是否遵守这些规则,这意味着您的代码可以编译。但是,您的代码仍然被破坏。(毕竟,"它编译了"并不意味着"它是正确的"(。

契约中的一条规则是比较运算是交换和关联的:如果a.compareTo(b)返回负数,那么b.compareTo(a)必须返回正数(如果一个返回0,另一个也必须返回(,反之亦然。如果你不遵守合同,就会发生疯狂的事情。例如,您创建了一个TreeSet,结果出现了问题,这种代码可以开始打印false:

TreeSet<YourBrokenItem> set = new TreeSet<>();
set.add(a);
set.add(b);
System.out.println(set.contains(a)); // prints false?

然而,当您子类化并想要覆盖定义时,遵守契约(交换性(规则是不可能的。让我们实践一下:

class Parent implements Comparable<Parent> {
int x;
/** Sort on x */
@Override public int compareTo(Parent other) {
return Integer.compare(x, other.x);
}
}
class Child extends Parent implements Comparable<Child> {
int y;
/** Sort on x first; if those are equal, sort on y */
@Override public int compareTo(Child other) {
int c = Integer.compare(x, other.x);
if (c != 0) return c;
return Integer.compare(y, other.y);
}
}

上面的代码之所以不起作用,有两个原因:[A]它不能编译,[B]即使你用了泛型转换,代码也会破坏合同。而且永远都是,你无法修复[B]。

因此,与其深入研究泛型并解决它(你可以(,这是一个没有意义的问题——B无法解决,所以你想要的是不可能的,因此没有必要解释如何破解以使编译器接受它

不可能的原因是交换性规则。让我们考虑3个实例:

Parent p = new Parent(10);
Child c = new Child(10, 5);
Child d = new Child(10, 20);

你的意图很清楚,这种情况会发生:

System.out.println(c.compareTo(d)); // prints -1
System.out.println(d.compareTo(c)); // prints +1

然而,当涉及到p时会发生什么?请记住,Child表示它是Parent的一个子类型,这意味着Child的实例可以做Parent的实例可以做的一切,更多。Parent可以做的一件事是将自己与Parent的另一个实例进行比较。Child的实例也是Parent的实例,因此,您可以将Parent与Child进行比较,反之亦然。因此,p.compareTo(a)是有效的。类似地,您可以生成一个new TreeSet<Parent>(),当然也可以在此基础上调用.add(new Child())。毕竟,Child的实例肯定也是Parent的实例,这就是子类化的含义。

因此:

p.compareTo(c); // This returns 0 - and you can't stop that from happening!
p.compareTo(d); // so does this
// thus, given the above, this:
c.compareTo(d);
// MUST, BY CONTRACT, return 0!

因此,你想要什么?不可能的可比合同禁止这样做。

实际上,使用Comparable,您可以"选择一个级别",可比性是在该级别上严格定义的,子类不能改变这一点。无论类型层次结构中的哪种类型决定为implements Comparable,这就是级别。它们为这些东西设置了自然比较的基线,并且子类在不违反约定的情况下根本无法修改其工作方式

您可能想要的是忘记自然顺序,而使用比较器。每个按自然顺序工作的系统(如TreeSetlist.sort(Comparator.naturalOrder())Collections.sort(list)Arrays.binarySearch,依此类推(,也有一个接受Comparator<T>的重载变体。

CAN可以制作一个自定义比较器,专门用于比较儿童。如果您正确定义了比较操作(意味着有一个完整的顺序(,您甚至可以制作一个自定义比较器来比较子级父级。例如,通过说给定相等的x值,任何new Parent()实例都将始终在任何new Child()之前排序,而不管Child的y值:

Comparator<Parent> myCustomComparator = (a, b) -> {
int c = Integer.compare(a.x, b.x);
if (c != 0) return c; // x isn't equal so it controls.
if (a instanceof Child && !(b instanceof Child)) return +1;
if (!(a instanceof Child) && b instanceof Child) return -1;
if (!(a instanceof Child) && !(b instanceof Child)) return 0;
int y1 = ((Child) a).y, y2 = ((Child) b).y;
return Integer.compare(y1, y2);
};
TreeSet<Parent> myAwesomeSelfSortingSet = new TreeSet<Parent>(myCustomComparator);
// and voila.

最新更新