¿Cómo se lee el valor de un campo privado de una clase diferente en Java?

Tengo una clase mal diseñada en un JAR terceros y necesito acceder a uno de sus campos privados . Por ejemplo, ¿por qué debería elegir un campo privado si es necesario?

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

¿Cómo puedo usar la reflexión para obtener el valor de stuffIWant ?

Para acceder a campos privados, debe obtenerlos de los campos declarados de la clase y luego hacerlos accesibles:

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

EDITAR : como ha sido comentado por aperkins , tanto el acceso al campo, establecerlo como accesible y recuperar el valor arrojarán Exception , aunque las únicas excepciones comprobadas que debe tener en cuenta son los comentados anteriormente.

La NoSuchFieldException se lanzaría si solicitara un campo por un nombre que no correspondiera a un campo declarado.

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

La IllegalAccessException se lanzaría si el campo no fuera accesible (por ejemplo, si es privado y no se ha hecho accesible a través de la falta de la línea f.setAccessible(true) .

Los RuntimeException s que pueden lanzarse son SecurityException s (si el SecurityManager de JVM no le permite cambiar la accesibilidad de un campo), o IllegalArgumentException s, si intenta acceder al campo en un objeto que no IllegalArgumentException al tipo de clase del campo:

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

Pruebe FieldUtils de apache commons-lang3:

 FieldUtils.readField(object, fieldName, true); 

La reflexión no es la única forma de resolver su problema (que es acceder a la funcionalidad / comportamiento privado de una clase / componente)

Una solución alternativa es extraer la clase de .jar, descomstackrla utilizando (por ejemplo) Jode o Jad , cambiar el campo (o agregar un descriptor de acceso) y recomstackrlo contra el archivo .jar original. A continuación, coloque la nueva clase antes del .jar en classpath, o vuelva a insertarla en .jar . (la utilidad jar le permite extraer y reinsertar a un .jar existente)

Como se señala a continuación, esto resuelve el problema más amplio de acceder / cambiar el estado privado en lugar de simplemente acceder / cambiar un campo.

Esto requiere que .jar no se firme, por supuesto.

Otra opción que no se ha mencionado aún: usar Groovy . Groovy le permite acceder a variables de instancia privadas como un efecto secundario del diseño del idioma. Ya sea que tenga o no un getter para el campo, puede simplemente usar

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

Usando Reflection en Java puede acceder a todos los campos y métodos private/public de una clase a otra. Pero según la documentación de Oracle en los inconvenientes de la sección, recomendaron que:

“Dado que la reflexión permite que el código realice operaciones que serían ilegales en código no reflectivo, como el acceso a campos y métodos privados, el uso de la reflexión puede producir efectos secundarios inesperados, lo que puede hacer que el código resulte disfuncional y destruya la portabilidad. rompe abstracciones y por lo tanto puede cambiar el comportamiento con actualizaciones de la plataforma “

aquí está el siguiente código de snapts para demostrar conceptos básicos de Reflexión

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

Espero que ayude

Como lo menciona oxbow_lakes, puedes usar el reflection para evitar las restricciones de acceso (suponiendo que tu SecurityManager te lo permita).

Dicho esto, si esta clase está tan mal diseñada que te hace recurrir a tal piratería, tal vez deberías buscar una alternativa. Claro que este pequeño truco podría ahorrarte algunas horas, pero ¿cuánto te costará en el futuro?

Utilice el marco de optimización de Soot Java para modificar directamente el bytecode. http://www.sable.mcgill.ca/soot/

El hollín está escrito completamente en Java y funciona con nuevas versiones de Java.

Debes hacer lo siguiente:

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

Solo una nota adicional sobre la reflexión: he observado en algunos casos especiales, cuando varias clases con el mismo nombre existen en diferentes paquetes, que la reflexión, tal como se utiliza en la respuesta superior, puede no seleccionar la clase correcta del objeto. Por lo tanto, si sabe cuál es el package.class del objeto, entonces es mejor acceder a sus valores de campo privados de la siguiente manera:

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

(Esta es la clase de ejemplo que no funcionaba para mí)

Si usa Spring, ReflectionTestUtils proporciona algunas herramientas prácticas que ayudan aquí con un mínimo esfuerzo. Se describe como “para su uso en escenarios de prueba de integración y unidades” . También hay una clase similar llamada ReflectionUtils, pero se describe como “Solo para uso interno” . Consulte esta respuesta para obtener una interpretación de lo que esto significa.

Para abordar el ejemplo publicado:

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