Переопределение метода с использованием стирания типа

Сегодня я наткнулся на что-то интересное. Предположим, что следующий класс 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) предупреждение. Если вам было позволено делать то, что вы хотели, вышеизложенное исключало бы исключение.