Как прочитать значение частного поля из другого класса в Java?
у меня плохо спроектированный класс в 3-й партии JAR
и мне нужно получить доступ к одной из его частная поля. Например,
почему мне нужно выбрать частное поле, это необходимо?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
как я могу использовать отражение, чтобы получить значение stuffIWant
?
10 ответов:
чтобы получить доступ к закрытым полям, вам нужно получить их из класса объявил поля, а затем сделать их доступными:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException f.setAccessible(true); Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
EDIT: как было прокомментировано aperkins, как доступ к полю, установив его как доступный и извлекая значение будет все бросить
Exception
s, хотя только проверил исключения, о которых вы должны помнить, прокомментированы выше.The
NoSuchFieldException
будет будет брошен, если вы запросили поле по имени, которое не соответствует объявленному полю.obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
The
IllegalAccessException
будет выброшено, если поле не было доступно (например, если оно является частным и не было доступно через пропускf.setAccessible(true)
линии.The
RuntimeException
s, которые могут быть брошены либоSecurityException
s (Если JVMSecurityManager
не позволит вам изменить доступность поля), илиIllegalArgumentException
s, Если вы попытаетесь получить доступ к полю на объекте нет типа класса поля:f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
попробовать
FieldUtils
от Apache commons-lang3:FieldUtils.readField(object, fieldName, true);
отражение-это не единственный способ решить вашу проблему (которая заключается в доступе к частной функциональности/поведению класса/компонента)
альтернативным решением является извлечение класса из .jar, декомпилируйте его с помощью (скажем)Джоуд или Jad, измените поле (или добавьте метод доступа) и перекомпилируйте его с оригиналом .сосуд. Тогда ставьте новые .класс впереди
.jar
в classpath, или вставьте его в.jar
. (утилита jar позволяет чтобы извлечь и повторно вставить в существующий .банку)как отмечено ниже, это решает более широкую проблему доступа / изменения частного состояния, а не просто доступа/изменения поля.
для этого требуется
.jar
не подписываться, конечно.
еще один вариант, который еще не был упомянут: используйте в Groovy. Groovy позволяет получить доступ к частным переменным экземпляра в качестве побочного эффекта дизайна языка. Ли у вас есть геттер для поля, вы можете просто использовать
def obj = new IWasDesignedPoorly() def hashTable = obj.getStuffIWant()
С помощью отражение в Java вы можете получить доступ ко всем
private/public
поля и методы из одного класса в другой .Но в соответствии с Oracleдокументация в разделе недостатки рекомендовано :" поскольку отражение позволяет коду выполнять операции, которые были бы незаконными в неотражающем коде, такие как доступ к частным полям и методам, использование отражения может привести к неожиданным побочным эффектам, который может сделать код дисфункциональным и может разрушить переносимость. Отражающий код нарушает абстракции и поэтому может изменить поведение с обновлением платформы"
вот следующий код snapts, чтобы продемонстрировать основные понятия отражение
Reflection1.java
public class Reflection1{ private int i = 10; public void methoda() { System.out.println("method1"); } public void methodb() { System.out.println("method2"); } public void methodc() { System.out.println("method3"); } }
Reflection2.java
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Reflection2{ public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method[] mthd = Reflection1.class.getMethods(); // for axis the methods Field[] fld = Reflection1.class.getDeclaredFields(); // for axis the fields // Loop for get all the methods in class for(Method mthd1:mthd) { System.out.println("method :"+mthd1.getName()); System.out.println("parametes :"+mthd1.getReturnType()); } // Loop for get all the Field in class for(Field fld1:fld) { fld1.setAccessible(true); System.out.println("field :"+fld1.getName()); System.out.println("type :"+fld1.getType()); System.out.println("value :"+fld1.getInt(new Reflaction1())); } } }
надеюсь, что это поможет.
Как упоминает oxbow_lakes, вы можете использовать отражение, чтобы обойти ограничения доступа (при условии, что ваш SecurityManager позволит вам).
тем не менее, если этот класс настолько плохо спроектирован, что заставляет вас прибегать к такому хакерству, возможно, вам следует искать альтернативу. Конечно, этот маленький хак может сэкономить вам несколько часов сейчас, но сколько это будет стоить вам в будущем?
используйте soot Java Optimization framework для непосредственного изменения байт-кода. http://www.sable.mcgill.ca/soot/
сажа полностью написана на Java и работает с новыми версиями Java.
вам нужно сделать следующее:
private static Field getField(Class<?> cls, String fieldName) { for (Class<?> c = cls; c != null; c = c.getSuperclass()) { try { final Field field = c.getDeclaredField(fieldName); field.setAccessible(true); return field; } catch (final NoSuchFieldException e) { // Try parent } catch (Exception e) { throw new IllegalArgumentException( "Cannot access field " + cls.getName() + "." + fieldName, e); } } throw new IllegalArgumentException( "Cannot find field " + cls.getName() + "." + fieldName); }
просто дополнительная заметка об отражении: я наблюдал в некоторых особых случаях, когда несколько классов с одинаковым именем существуют в разных пакетах, что отражение, используемое в верхнем ответе, может не выбрать правильный класс из объекта. Так что если вы знаете, что такое пакет.класс объекта, то лучше получить доступ к его частным значениям поля следующим образом:
org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0); Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver"); f.setAccessible(true); Solver s = (Solver) f.get(ll);
(Это пример класса, который не работал для меня)
при использовании Spring, ReflectionTestUtils предоставляет некоторые удобные инструменты, которые помогают здесь с минимальными усилиями. Это описывается как "для использования в сценариях модульного и интеграционного тестирования". Существует также аналогичный класс с именем ReflectionUtils но это описывается как "только для внутреннего использования" - см. ответ для интерпретации того, что это означает.
для обращения к опубликованному пример:
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");