为什么我不能为一个继承另一个的 类 实现 Comparable?
Why I can't implement Comparable for both the classes where one inherits other?
假设我有两个 类 人和雇员。人正在实施 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 的原因。因此,将为 类 添加一个 Bridge 方法,它将 Object o
作为参数。这是不允许的。我的理解对吗?
我的愚蠢问题是:为什么不能像函数重写那样处理它?
关于覆盖和过载的简短回答
您的理解是全球正确的。您不能同时实施 Comparable<Person>
和 Comparable<Employee>
。由于类型擦除,这基本上会导致两个方法 int compareTo(Object)
具有相同的名称和签名,这是不允许的。
但是,对于您的第二个方法 int compareTo(Employee)
,它并不是一个覆盖恰恰是因为一个对象,顺便说一下,一个人,并不总是一个雇员。需要显式强制转换。因此,这两个方法没有相同的签名,因此第二个方法不是第一个方法的重写。
如果您删除 @Override
注释,没关系。您的方法不是 override,但它是完全有效的 overload.
提醒一下,
- 覆盖 正在用子class 中的另一个替换一个方法。覆盖方法必须具有相同名称和签名(return类型协方差除外)。
- 重载 是在同一个 class 中使用多个同名 的方法。这些方法必须具有不同的签名。
关于为什么不允许的详细回答
暂时假设允许实施 Comparable<Person>
和 Comparable<Employee>
。
编译器在 class Person:
中生成这个桥接方法
public int compareTo (Object o) {
return compareTo((Person)o);
}
编译 class 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。
除了能够对不同类型的元素进行排序这一有争议的问题之外,您真的期待它吗?
现在假设编译器不会在 class 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
假设我有两个 类 人和雇员。人正在实施 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 的原因。因此,将为 类 添加一个 Bridge 方法,它将 Object o
作为参数。这是不允许的。我的理解对吗?
我的愚蠢问题是:为什么不能像函数重写那样处理它?
关于覆盖和过载的简短回答
您的理解是全球正确的。您不能同时实施 Comparable<Person>
和 Comparable<Employee>
。由于类型擦除,这基本上会导致两个方法 int compareTo(Object)
具有相同的名称和签名,这是不允许的。
但是,对于您的第二个方法 int compareTo(Employee)
,它并不是一个覆盖恰恰是因为一个对象,顺便说一下,一个人,并不总是一个雇员。需要显式强制转换。因此,这两个方法没有相同的签名,因此第二个方法不是第一个方法的重写。
如果您删除 @Override
注释,没关系。您的方法不是 override,但它是完全有效的 overload.
提醒一下,
- 覆盖 正在用子class 中的另一个替换一个方法。覆盖方法必须具有相同名称和签名(return类型协方差除外)。
- 重载 是在同一个 class 中使用多个同名 的方法。这些方法必须具有不同的签名。
关于为什么不允许的详细回答
暂时假设允许实施 Comparable<Person>
和 Comparable<Employee>
。
编译器在 class Person:
中生成这个桥接方法public int compareTo (Object o) {
return compareTo((Person)o);
}
编译 class 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。 除了能够对不同类型的元素进行排序这一有争议的问题之外,您真的期待它吗?
现在假设编译器不会在 class 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