Comment lire la valeur d’un champ privé d’une classe différente en Java?

J’ai une classe mal conçue dans un JAR tiers et je dois accéder à l’un de ses champs privés . Par exemple, pourquoi devrais-je choisir le domaine privé est-il nécessaire?

 class IWasDesignedPoorly { private Hashtable stuffIWant; } IWasDesignedPoorly obj = ...; 

Comment puis-je utiliser la reflection pour obtenir la valeur de stuffIWant ?

    Pour accéder aux champs privés, vous devez les extraire des champs déclarés de la classe, puis les rendre accessibles:

     Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException f.setAccessible(true); Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException 

    EDIT : comme cela a été commenté par aperkins , les deux access au champ, en le définissant comme accessible et en récupérant la valeur renverront tous des Exception , bien que les seules exceptions vérifiées dont vous devez tenir compte soient commentées ci-dessus.

    Le NoSuchFieldException serait NoSuchFieldException si vous demandiez un champ par un nom qui ne correspondait pas à un champ déclaré.

     obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException 

    L’ IllegalAccessException serait levée si le champ n’était pas accessible (par exemple, s’il est privé et n’a pas été rendu accessible via l’absence de la ligne f.setAccessible(true) .

    Les RuntimeException qui peuvent être RuntimeException sont SecurityException s (si SecurityException la JVM ne vous permet pas de modifier l’accessibilité d’un champ) ou IllegalArgumentException s, si vous essayez d’accéder au champ sur un object qui n’est pas du type de la classe du champ:

     f.get("BOB"); //will throw IllegalArgumentException, as Ssortingng is of the wrong type 

    Essayez FieldUtils partir de apache commons-lang3:

     FieldUtils.readField(object, fieldName, true); 

    Reflection n’est pas le seul moyen de résoudre votre problème (qui consiste à accéder aux fonctionnalités / comportements privés d’une classe / d’un composant)

    Une autre solution consiste à extraire la classe du fichier .jar, à la décomstackr en utilisant (disons) Jode ou Jad , à modifier le champ (ou à append un accesseur) et à le recomstackr avec le fichier .jar d’origine. Ensuite, placez le nouveau .class avant le .jar dans le classpath ou réinsérez-le dans le .jar . (l’utilitaire jar vous permet d’extraire et de réinsérer dans un fichier .jar existant)

    Comme indiqué ci-dessous, cela résout la question plus large de l’access / de la modification de l’état privé plutôt que de simplement accéder / changer un champ.

    Cela nécessite bien sûr que le .jar ne soit pas signé.

    Une autre option qui n’a pas encore été mentionnée: utilisez Groovy . Groovy vous permet d’accéder aux variables d’instance privées en tant qu’effet secondaire de la conception du langage. Que vous ayez ou non un getter pour le terrain, vous pouvez simplement utiliser

     def obj = new IWasDesignedPoorly() def hashTable = obj.getStuffIWant() 

    En utilisant la reflection en Java, vous pouvez accéder à tous les champs et méthodes private/public d’une classe à une autre. Mais, conformément à la documentation Oracle dans les inconvénients de la section, ils ont recommandé que:

    “Puisque la reflection permet au code d’effectuer des opérations qui seraient illégales dans un code non réfléchi, telles que l’access à des champs et méthodes privés, l’utilisation de la reflection peut entraîner des effets secondaires inattendus, susceptibles de perturber le code et de détruire la portabilité. brise les abstractions et peut donc changer le comportement avec les mises à niveau de la plate-forme “

    voici le code suivant pour illustrer les concepts de base de la reflection

    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(Ssortingng 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())); } } } 

    J’espère que ça va aider.

    Comme le mentionne oxbow_lakes, vous pouvez utiliser la reflection pour contourner les ressortingctions d’access (en supposant que votre SecurityManager vous le permette).

    Cela dit, si cette classe est tellement mal conçue qu’elle vous incite à recourir à un tel piratage, vous devriez peut-être chercher une alternative. Bien sûr, ce petit hack pourrait vous faire économiser quelques heures maintenant, mais combien cela vous coûtera-t-il sur la route?

    Utilisez le framework d’optimisation Java Soot pour modifier directement le bytecode. http://www.sable.mcgill.ca/soot/

    Soot est entièrement écrit en Java et fonctionne avec les nouvelles versions de Java.

    Vous devez effectuer les opérations suivantes:

     private static Field getField(Class cls, Ssortingng 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); } 

    Juste une remarque supplémentaire sur la reflection: j’ai observé dans certains cas particuliers, lorsque plusieurs classes du même nom existent dans des packages différents, que la reflection utilisée dans la réponse du haut risque de ne pas sélectionner la classe correcte dans l’object. Donc, si vous savez quelle est la package.class de l’object, alors il est préférable d’accéder aux valeurs de son champ privé comme suit:

     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); 

    (Ceci est l’exemple de classe qui ne fonctionnait pas pour moi)

    Si vous utilisez Spring, ReflectionTestUtils fournit des outils pratiques qui vous aident ici avec un effort minimal. Il est décrit comme étant “à utiliser dans des scénarios de tests unitaires et d’intégration” . Il existe également une classe similaire nommée ReflectionUtils, mais celle-ci est décrite comme “destinée uniquement à un usage interne” – voir cette réponse pour une interprétation de ce que cela signifie.

    Pour répondre à l’exemple affiché:

     Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");