Suppose I have two classes Person and Employee. Person is implementing Comparable. The moment I try to implement Comparable for Employee as well I get a compiler error. My code is:
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 error is:
The interface Comparable cannot be implemented more than once with different arguments: Comparable<Hierarchy.Person> and Comparable<Hierarchy.Employee>
I understand that reason for this is Type Erasures. Because of that a Bridge method would be added for both classes which will take Object o
as argument. And that is not allowed. Is my understanding correct here?
My silly question is: Why can't that get handled like function overriding?
Your understanding is globally correct. You can't implement both Comparable<Person>
and Comparable<Employee>
. Because of type erasure, this would basically lead to two methods int compareTo(Object)
with the same name and signature, what isn't allowed.
However, for your second method int compareTo(Employee)
, it isn't an override precisely because an Object, and by the way neither a Person, isn't always an Employee. An explicit cast would be needed. Therefore, the two methods don't have the same signature, and so the second isn't an override of the first.
If you remove the @Override
annotation, it's fine though. Your method isn't an override, but it's a perfectly valid overload.
As a reminder,
Assume for a moment that implementing Comparable<Person>
and Comparable<Employee>
would be allowed.
The compiler generates this bridge method in the class Person:
public int compareTo (Object o) {
return compareTo((Person)o);
}
When compiling the class Employee, the compiler is similarly supposed to generate this one:
public int compareTo (Object o) {
return compareTo((Employee)o);
}
As explained above, int compareTo(Employee)
can't be an override of int compareTo(Person)
. However, the second bridge method in Employee above is obviously an override of the first one in Person.
The problems starts here.
Let's say we have this code:
List persons = new ArrayList();
persons.add(new Person(...));
person.add(new Employee(...));
person.add(new Employee(...));
persons.add(new Person(...));
...
Collections.sort(persons);
You are going to compare an Employee and a Person during the sort and a ClassCastException will be thrown. Outside the debattable question of being able to sort elements of different types, would you honnestly expect it?
Now let's assume that the compiler doesn't generate the overridding bridge method in class Employee, and that the list to sort exclusively contain objects of type Employee.
Your method int compareTo(Employee)
will never be called since the bridge method in Person only calls int compareTo(Person)
. NO exception will be thrown, but the code probably doesn't do what you expect.
So, what should the compiler do? Override the bridge method or not? Maybe one of the two solutions is acceptable in your particular case, but the compiler can't guesw which one, if any.
Let's take another example where the problem may be more obvious:
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) { ... }
}
Here, the compiler would have to decide whether to call the method of I1 or I2 inside its bridge method void B::m(Object)
. If you try to compile this code, you are given a better clue of what the problem is:
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