为什么我不能为一个继承另一个的两个类都实现Comparable? 关于覆盖和过载的简短答案关于为什么不允许它的更长的答案

问题描述

假设我有两个类Person和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),这并不是一个覆盖,这完全是因为对象以及顺便说一句都不是Person的员工也不总是雇员。需要显式的强制转换。因此,这两种方法没有相同的签名,因此第二种方法不是第一种方法的替代。

如果您删除了@Override注释,那很好。您的方法不是 override ,而是完全有效的 overload

提醒一下,

  • 覆盖将一个方法替换为子类中的另一个 。覆盖方法必须具有相同名称和签名(返回类型协方差除外)。
  • 重载正在同一类中使用几种具有相同名称的方法。这些方法必须具有不同的签名

关于为什么不允许它的更长的答案

暂时假设将允许实施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。 除了能够对不同类型的元素进行排序这一值得商bat的问题之外,您是否会真诚地期待它?

现在让我们假设编译器没有在Employee类中生成覆盖桥方法,并且要排序的列表仅包含Employee类型的对象。 您的方法int compareTo(Employee)将永远不会被调用,因为Person中的bridge方法仅调用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) { ... }
}

在这里,编译器必须决定是在其桥接方法void B::m(Object)中调用I1还是I2方法。如果您尝试编译此代码,则可以更好地了解问题所在:

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...