Почему я получаю методы базового класса через отражение, когда подкласс переопределяет их? - PullRequest
10 голосов
/ 05 сентября 2011

У меня супер класс как:

class MyClass<T> {
  public void setValue(T value){
    //insert code
  }

  public T getValue(){
    return null;
  }
}

тогда у меня есть конкретный вывод

class MyClassImp extends MyClass<String> {
  @Override
  public void setValue(String value){
    //insert code
  }

  @Override
  public String getValue(){
    return null;
  }
}

При отражении в MyClassImpl как:

Class clazz = MyClassImpl.class;
Method[] methods = clazz.getDeclaredMethods();

Я получаю реализацию суперкласса java.lang.Object getValue(), void setValue(java.lang.Object) и java.lang.String getValue(), void setValue(java.lang.String).

Согласно документации Java Class.getDeclaredMethods() vis-a-viz

Возвращает массив объектов Method, отражающих все методы, объявленные классом или интерфейсом, представленным этим объектом Class. Это включает в себя открытый, защищенный, доступ по умолчанию (пакетный) и закрытые методы, но исключает унаследованные методы. Элементы в возвращаемом массиве не сортируются и не располагаются в каком-либо определенном порядке. Этот метод возвращает массив длины 0, если класс или интерфейс не объявляют методы, или если этот объект Class представляет примитивный тип, класс массива или void. Метод инициализации класса <clinit> не включен в возвращаемый массив. Если класс объявляет несколько открытых методов-членов с одинаковыми типами параметров, все они включаются в возвращаемый массив.

Почему я получаю реализацию супертипа? Что-то мне не хватает?

Причина, по которой мне это нужно, заключается в том, что я рефлекторно вызываю setValue для реализации базового класса, к которому я добавил несколько специальных комментариев к комментариям и, конечно, дополнительные ограничения.

Ответы [ 2 ]

14 голосов
/ 05 сентября 2011

Это потому, что скомпилированный класс на самом деле действительно объявляет setValue(Object). Этот метод будет приведен к типу String, а затем вызовет строго типизированный метод. Точно так же getValue(Object) звонки getValue(String).

В основном это требуется, потому что JVM на самом деле не знает об обобщениях (по крайней мере, не в глубине) - чтобы переопределить метод суперкласса на уровне JVM, он должен иметь такую ​​же сигнатуру.

Посмотрите на класс с javap -c MyclassImp, вы увидите дополнительные синтетические методы:

public java.lang.Object getValue();
  Code:
   0:   aload_0
   1:   invokevirtual   #3; //Method getValue:()Ljava/lang/String;
   4:   areturn

public void setValue(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   checkcast       #4; //class java/lang/String
   5:   invokevirtual   #5; //Method setValue:(Ljava/lang/String;)V
   8:   return

}
1 голос
/ 05 сентября 2011

Как сказал ранее Джон, информация типа теряется во время выполнения для шаблонов .

Таким образом, всякий раз, когда вы используете обобщенные элементы, компилятор помещает все эти «обобщенные» унаследованные методы в подкласс.То же самое не верно с неуниверсальным кодом.

Я только что проверил: когда я удалил общий код (часть <T>) из суперкласса и код отражения дал мне ровно два метода в подклассе, хотя он переопределяет их.Это подразумевает, что документация должна быть немного явной для родового кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...