java - 为什么我不能为继承另一个的两个类实现 Comparable?
问题描述
假设我有两个类 Person 和 Employee。人正在实施 Comparable。当我尝试为 Employee 实现 Comparable 时,我得到了一个编译器错误。我的代码是:
class Person implements Comparable<Person>{
protected int age;
public Person(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
//if( o instanceof Employee) return 0;
return age - o.age;
}
public String toString() {
return ""+age;
}
}
class Employee extends Person implements Comparable<Employee>{
public Employee(int age) {
super(age);
}
public String toString() {
return ""+age;
}
}
错误是:
The interface Comparable cannot be implemented more than once with different arguments: Comparable<Hierarchy.Person> and Comparable<Hierarchy.Employee>
我知道原因是Type Erasures。因此,将为两个将Object o
作为参数的类添加一个 Bridge 方法。这是不允许的。我的理解在这里正确吗?
我愚蠢的问题是:为什么不能像函数覆盖一样处理?
解决方案
关于覆盖和过载的简短回答
您的理解在全球范围内是正确的。您不能同时实现Comparable<Person>
和Comparable<Employee>
。由于类型擦除,这基本上会导致两个方法int compareTo(Object)
具有相同的名称和签名,这是不允许的。
但是,对于您的第二种方法int compareTo(Employee)
,它并不是一个重写,因为一个对象,顺便说一下,一个人,并不总是一个雇员。需要一个明确的演员表。因此,这两个方法没有相同的签名,因此第二个不是第一个的覆盖。
如果您删除@Override
注释,那很好。您的方法不是override,但它是一个完全有效的重载。
提醒一句,
- 覆盖是用子类中的另一个方法替换一个方法。覆盖方法必须具有相同的名称和签名(返回类型协方差除外)。
- 重载是在同一个类中使用多个具有相同名称的方法。这些方法必须有不同的签名。
关于为什么不允许这样做的更长答案
暂时假设实施Comparable<Person>
并将Comparable<Employee>
被允许。
编译器在 Person 类中生成这个桥接方法:
public int compareTo (Object o) {
return compareTo((Person)o);
}
在编译 Employee 类时,编译器同样应该生成这个:
public int compareTo (Object o) {
return compareTo((Employee)o);
}
如上所述,int compareTo(Employee)
不能覆盖int compareTo(Person)
. 但是,上面 Employee 中的第二个桥接方法显然是对 Person 中第一个的覆盖。问题从这里开始。
假设我们有以下代码:
List persons = new ArrayList();
persons.add(new Person(...));
person.add(new Employee(...));
person.add(new Employee(...));
persons.add(new Person(...));
...
Collections.sort(persons);
您将在排序过程中比较 Employee 和 Person 并且将抛出 ClassCastException。除了能够对不同类型的元素进行排序这个有争议的问题之外,你会诚实地期待它吗?
现在让我们假设编译器没有在 Employee 类中生成覆盖桥接方法,并且要排序的列表只包含 Employee 类型的对象。您的方法int compareTo(Employee)
将永远不会被调用,因为 Person 中的桥接方法只调用int compareTo(Person)
. 不会抛出任何异常,但代码可能不会像您期望的那样。
那么,编译器应该怎么做呢?是否覆盖桥接方法?在您的特定情况下,这两种解决方案中的一种可能是可以接受的,但编译器无法猜测是哪一种(如果有的话)。
让我们再举一个问题可能更明显的例子:
interface I1<T> {
public void m (T t);
}
interface I2<U> {
public void m (U u);
}
class A implements I1<A> {
@Override public void m (A a) { ... }
}
class B extends A implements I2<B> {
@Override public void m (B b) { ... }
}
在这里,编译器必须决定是在其桥接方法中调用 I1 还是 I2 的方法void B::m(Object)
。如果您尝试编译此代码,您会更好地了解问题所在:
error: name clash: class B has two methods with the same erasure, yet neither overrides the other
@Override public void m (B b) {
^
first method: m(B) in I2
second method: m(A) in I1
推荐阅读
- jquery - Jquery DataTable 插件没有效果
- reactjs - 反应js中的方法覆盖
- sql - 实现基于值范围的颜色查找 SQL 表
- vaadin - Vaadin-CDI 缺少 Bundle-ManifestVersion
- r - 如何通过在R中搜索特定字母和字母顺序来重命名变量并将其更改为另一个变量
- azure-devops - 使用 Fortify on Demand 进行身份验证时出错。错误:错误:无法验证第一个证书
- c# - 这个活动图是否很好地显示了我的方法是如何工作的?
- python - 对 CX_ORACLE 的 SQL INSERT 语句使用 Python dict
- vue.js - 如何在 Nuxt JS 中将 vuetify 更新到最新版本?
- python - 来自 2d numpy 数组的边界框