我正在创建子类并在其中包含一些类元素。现在我想重写超类中的compareTo方法,并使用子类中的变量作为新compareTo方法中的参数,但我收到错误消息,指出compareTo方法的参数必须与超类中的参数相同。
这个问题有什么解决办法吗?
提前致谢!
我尝试过的解决方案是:
1)只是简单地在子类中编写新方法(而不是覆盖超类中的方法)2)使用我想要的参数在超类中创建新的compareTo方法
这样程序就可以工作,但我觉得这不是解决这个问题的正确方法
任何人都可以对此提出建议或提供建议吗?
我正在创建子类并在其中包含一些类元素。
好的。
现在我想重写超类中的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?
Run Code Online (Sandbox Code Playgroud)
但是,当您子类化并想要覆盖定义时,遵守契约(交换性)规则是不可能的。让我们把它实践一下:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码不起作用有两个原因:[A]它无法编译,[B]即使你用巫毒魔法消除了泛型转换,代码也会破坏契约。而且永远都是,你无法修复[B]。
因此,与其深入研究泛型并解决它(你可以),这是一个有争议的问题 - B 无法解决,所以你想要的东西是不可能的,因此没有必要解释如何破解以便编译器接受它。
这是不可能的原因是交换律。让我们考虑 3 个实例:
Parent p = new Parent(10);
Child c = new Child(10, 5);
Child d = new Child(10, 20);
Run Code Online (Sandbox Code Playgroud)
你的意图非常明确,那就是发生这种情况:
Parent p = new Parent(10);
Child c = new Child(10, 5);
Child d = new Child(10, 20);
Run Code Online (Sandbox Code Playgroud)
然而,当p参与进来时会发生什么呢?请记住,Child它是子类型,Parent这意味着 Child 的实例可以执行 Parent 实例可以执行的所有操作,甚至更多。Parent 可以做的一件事是将其自身与 Parent 的另一个实例进行比较。Child 的实例也是 Parent 的实例,因此,您可以将 Parent 与 Child 进行比较,反之亦然。因此,p.compareTo(a)有效。同样,您可以创建 anew 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!
Run Code Online (Sandbox Code Playgroud)
因此,你想要什么?不可能的。可比合同禁止这样做。
实际上,使用 Comparable,您可以“选择一个级别”,并且可比性是严格在该级别上定义的,子类无法更改它。无论类型层次结构中的类型决定什么implements Comparable,这就是级别。他们为这些事物设定了自然比较的基线,而子类在不违反合同的情况下根本无法修改其工作方式。
您可能想要的是忘记自然顺序并使用比较器。每个按自然顺序工作的系统(例如TreeSet、 或list.sort(Comparator.naturalOrder())或Collections.sort(list)等Arrays.binarySearch)也有一个接受Comparator<T>.
您可以制作一个自定义比较器来专门比较儿童。如果您正确定义比较操作(意味着有完整的顺序),您甚至可以创建一个自定义比较器来比较子级和父级。例如,通过说给定相等的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.
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
305 次 |
| 最近记录: |