#java #reflection
Вопрос:
У меня есть родительский класс, у которого есть метод, использующий отражение для извлечения и обновления полей. Этот метод будет вызван дочерним классом
package com.torchai.service.common.response;
import java.lang.reflect.Field;
public class TestReflection {
private String foo;
public void update() throws IllegalAccessException {
final Field[] fields = getClass().getDeclaredFields();
for (final Field f : fields) {
// f.setAccessible(true);
// final Object value = response.get(f.getName());
Object value = "new value";
f.set(this, value);
}
}
public static void main(final String[] args) throws IllegalAccessException {
final TestReflection t = new TestReflection() {
private String abc;
};
t.update();
}
}
Когда дочерний класс (в данном случае анонимный класс) вызывает метод, который наследуется от родителя, значение this
является дочерним классом. Из-за этого я подумал, что не будет проблем с извлечением и настройкой личных полей. Оказывается, getDeclaredFields()
он может извлекать private
поля, но Field.set()
не может их обновлять, если я setAccessible
не верну значение true. Является ли это правильным поведением? Почему дочерний класс не может установить свои собственные поля? Если я могу видеть поле (т. Е. Я могу получить закрытое поле), разве я не должен иметь возможность обновить его?
Исключение, которое я получаю, это
java.lang.IllegalAccessException: Class com.torchai.service.common.response.TestReflection can not access a member of class com.torchai.service.common.response.TestReflection$1 with modifiers "private"
Обратите внимание, что это подразумевает, что именно родительский класс пытается получить доступ к дочернему классу. Почему родительский класс считается вызывающим?
Если я изменю основной метод, чтобы исключить использование дочернего класса
TestReflection t = new TestReflection();
t.update()
тогда все работает так, как и ожидалось. Класс обновляет свои собственные поля, поэтому setAccessbile
в этом нет необходимости
Ответ №1:
Код, который пытается получить доступ к полю, находится внутри класса TestReflection
, и это все, что имеет значение. Нет никаких признаков того, что подкласс готов предоставить своему суперклассу доступ к своему полю, поскольку private
это не подразумевает доступа из любого другого класса. Подкласс просто вызывает метод update()
своего суперкласса, не зная, что этот метод будет делать внутри, поэтому подкласс не пытается получить доступ к полю подкласса здесь.
Если быть более точным, в вашем конкретном примере подкласс является вложенным классом суперкласса, поэтому доступ private
к членам друг друга разрешен, но вы столкнулись со старым недостатком Отражения, который существовал до тех пор, пока существуют вложенные классы. При компиляции примера с помощью целевого пакета JDK 11 или более поздней версии этот конкретный пример будет работать.
Но для классов верхнего уровня private
это означает, что ни один другой класс не может получить к нему доступ (без предварительного включения переопределения доступа), независимо от того, является ли он подклассом или суперклассом класса объявления. Что, похоже, и есть ваш настоящий вопрос.
Обратите внимание, что использование подобной конструкции getClass().getDeclaredFields()
довольно необычно. Еще больше зацикливается на обнаруженных полях и просто устанавливает их все. Обычно вы хотите знать, к какому классу и полю вы обращаетесь. В вашем примере вы пытаетесь задать поле abc
подкласса, но когда вы меняете пример на new TestReflection().update()
, вы устанавливаете поле foo
TestReflection
вместо.