Переопределение метода с использованием стирания типа
Сегодня я наткнулся на что-то интересное.
Предположим, что следующий класс Java 6:
public class Ereasure {
public Object get(Object o) {
return null; // dummy
}
public static class Derived<T> extends Ereasure{
// (1)
@Override
public Object get(T o) {
return super.get(o);
}
// (2)
/*
@Override
public Object get(Object o) {
return super.get(o);
}*/
}
}
Если вы попытаетесь скомпилировать приведенный выше пример, компилятор говорит Ereasure.java:9: метод не переопределяет или не реализует метод из супертипа @Override
Если вы удалите аннотацию @Override (которая не должна быть необходимой!), Это говорит Ereasure.java:8: имя clash: get (T) в Ereasure.Derived и get (java.lang.Object) в Ereasure имеют одно и то же стирание, но не переопределяют другое
Это немного противоречиво, так как T должен erease Object и поэтому переопределять метод родительских классов get.
Если вы оставите (1) unannotated и uncomment (2), так что (1) перегрузки (2) это тоже не сработает.
Выход компилятора:
Ereasure.java:15: get(T) is already defined in Ereasure.Derived
public Object get(Object o) {
Как вывод, T убирается Object, но не может переопределить родительский метод get.
Теперь мой вопрос: почему dooes не по крайней мере один из примеров компилируется?
Ответы
Ответ 1
При простой догадке компилятор не использует общий вид при вычислении перегрузок, что, конечно, не имеет смысла, потому что иногда T может быть объектом другого временного другого. Затем переопределение становится зависимым от движущейся цели T, которая совершенно неверна, особенно если существует несколько методов, все называемые "get", но с разными типами параметров. В таком случае это просто не имеет смысла, и, предположив, они решили просто держать вещи простыми.
Ответ 2
В приведенном ниже примере вы можете увидеть, почему невозможно сделать то, что вы хотите:
public class Erasure {
public void set(Object o) {
return;
}
// method overloading: (which is valid)
public void set(String s) {
return;
}
public static class Derived<S> extends Erasure {
// Oops... which one am I supposed to override?
// (It would actually be overloading if S was a concrete type
// that is neither Object nor String.)
@Override
public void set(S o) { // does not compile
super.set(o);
}
}
}
Решение вашей проблемы состоит в том, что Erasure
должен быть параметризованным классом.
Ответ 3
Рассмотрим случай, когда у вас есть как getter, так и setter, переопределенные как generics.
Derived<String> d = new Derived<String();
Erasure e = d;
e.set(new Object());
String s = d.get(); //Class cast exception
Основной принцип генериков заключается в том, что исключение класса cast может произойти только в том случае, если существует либо (a) явное литье, либо (b) предупреждение. Если вам было позволено делать то, что вы хотели, вышеизложенное исключало бы исключение.