Переопределение метода с использованием стирания типов - PullRequest
10 голосов
/ 31 августа 2011

Сегодня я наткнулся на что-то интересное.Предположим, что следующий класс 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: name clash: get (T) в Ereasure.Derived и get (java.lang.Object) в Ereasure имеют одинаковое стирание, но нипереопределяет другие. Это немного противоречиво, поскольку T должен уменьшиться до Object и для этого переопределить родительский класс get.

Если вы оставите (1) без комментариев и раскомментируйте (2), так (1) перегрузит (2)это тоже не сработает.Вывод компилятора:

Ereasure.java:15: get(T) is already defined in Ereasure.Derived
  public Object get(Object o) {

В заключение, T уменьшается до Object, но не может переопределить родительский метод get.

Теперь у меня вопрос, почему не хотя бы один изпримеры компилируются?

Ответы [ 3 ]

7 голосов
/ 31 августа 2011

Вы можете увидеть в примере ниже, почему невозможно делать то, что вы хотите:

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 должен быть параметризованным классом.

2 голосов
/ 31 августа 2011

По простому предположению, компилятор не использует универсальное представление при вычислении перегрузок, что, конечно, не имело бы смысла, потому что иногда T может быть Object, а иногда его другого типа.Затем переопределение станет зависимым от движущейся цели T, что совершенно неверно, особенно если бы существовало несколько методов, которые все назывались «get», но с разными типами одного параметра.В таком случае это просто не имело бы смысла, и, по-видимому, они решили просто все упростить.

1 голос
/ 31 августа 2011

Рассмотрим случай, когда у вас есть и геттер, и сеттер, переопределенные как обобщенные.

Derived<String> d = new Derived<String();
Erasure e = d;
e.set(new Object());
String s = d.get(); //Class cast exception

Основополагающим принципом обобщений является то, что исключение приведения класса может произойти, только если есть (а) явное приведение или (b) предупреждение. Если бы вам было позволено делать то, что вы хотели, вышеприведенное выдало бы исключение без того или другого.

...