2009-04-09 24 views
30

¿Cómo puedo acceder a un campo de protección heredado de un objeto mediante la reflexión?Java reflexión - Acceso campo de protección

+2

La pregunta sería mejor si usted indicó lo que probaste (exactamente) y lo que sucedió (exactamente). –

Respuesta

50

Dos cuestiones que pueda tener problemas con - el campo podrían no ser accesibles normalmente (privada), y no es de la clase que usted está viendo, pero en algún lugar en la jerarquía.

Algo como esto podría funcionar incluso con estas cuestiones:

public class SomeExample { 

    public static void main(String[] args) throws Exception{ 
    Object myObj = new SomeDerivedClass(1234); 
    Class myClass = myObj.getClass(); 
    Field myField = getField(myClass, "value"); 
    myField.setAccessible(true); //required if field is not normally accessible 
    System.out.println("value: " + myField.get(myObj)); 
    } 

    private static Field getField(Class clazz, String fieldName) 
     throws NoSuchFieldException { 
    try { 
     return clazz.getDeclaredField(fieldName); 
    } catch (NoSuchFieldException e) { 
     Class superClass = clazz.getSuperclass(); 
     if (superClass == null) { 
     throw e; 
     } else { 
     return getField(superClass, fieldName); 
     } 
    } 
    } 
} 

class SomeBaseClass { 
    private Integer value; 

    SomeBaseClass(Integer value) { 
    this.value = value; 
    } 
} 

class SomeDerivedClass extends SomeBaseClass { 
    SomeDerivedClass(Integer value) { 
    super(value); 
    } 
} 
+5

Si hay un administrador de seguridad, esto fallaría. Necesitas concluir la llamada a setAccessible y getDeclaredField en una PriviledgedAction, y ejecutarla a través de java.security.AccessController.doPrivileged (...) – Varkhan

+0

te amo con todo mi corazón –

+0

Esto no funciona en Android :(Mi clase el árbol se ve así A-> B-> C y dentro de CI no puede obtener el valor de un campo protegido declarado en A. Tenga en cuenta que las tres clases están en paquetes diferentes. ¿Alguna idea de cómo evitar esto? – Moonwalker

4
field = myclass.getDeclaredField("myname"); 
field.setAccessible(true); 
field.set(myinstance, newvalue); 
+1

Esto puede no funcionar si hay un administrador de seguridad que bloquea el permiso correspondiente. –

+0

getField (String name) solo los campos públicos. – Moro

+0

@Moro: gracias, corregí el código –

0

¿Usted tal vez decir a partir de un objeto diferente de un contexto que no se confía con un conjunto SecurityManager? Eso rompería el sistema de tipos, por lo que no puedes. Desde un contexto de confianza, puede llamar al setAccessible para anular el sistema de tipos. Idealmente, no uses el reflejo.

+0

"Idealmente, no use reflejo". ¿Por qué? El OP está específicamente tratando de usar la reflexión ... mientras que él no dice por qué, hay muchos usos legítimos para la reflexión (especialmente en el código de prueba). –

+0

Si bien hay usos legítimos de la reflexión, la mayoría no lo son. En particular, si te estás arrastrando por la capacidad heredada de respetar las reglas de acceso al lenguaje Java (1.0), probablemente no lo necesites. –

+0

@Tom ... a menos que intente escribir casos de prueba JUnit específicos sin perder las reglas de acceso simplemente para el caso de prueba –

0

Se podría hacer algo así ...

Class clazz = Class.forName("SuperclassObject"); 

Field fields[] = clazz.getDeclaredFields(); 

for (Field field : fields) { 
    if (field.getName().equals("fieldImLookingFor")) { 
     field.set...() // ... should be the type, eg. setDouble(12.34); 
    } 
} 

También podría ser necesario cambiar la accesibilidad, como se señala en la respuesta de Maurice.

+0

Devuelve una matriz de objetos de campo que refleja todos los campos declarados por la clase o interfaz representada por este objeto de clase. No pasó por las súper clases – Moro

+0

¿Podría ser más específico con el problema? ¿Estás diciendo que solo obtuvo los campos en tu subclase? ¿Se ha asegurado de que al método Class.forName() se le haya pasado el nombre de la superclase y de que se haya modificado la accesibilidad, como sugirió Maurice? –

6

utilizar la reflexión para tener acceso a los miembros de la instancia de clase, que sean accesibles y establecer sus respectivos valores. Por supuesto, debe saber el nombre de cada miembro que desea cambiar, pero supongo que eso no será un problema.

public class ReflectionUtil { 
    public static Field getField(Class clazz, String fieldName) throws NoSuchFieldException { 
     try { 
      return clazz.getDeclaredField(fieldName); 
     } catch (NoSuchFieldException e) { 
      Class superClass = clazz.getSuperclass(); 
      if (superClass == null) { 
       throw e; 
      } else { 
       return getField(superClass, fieldName); 
      } 
     } 
    } 
    public static void makeAccessible(Field field) { 
     if (!Modifier.isPublic(field.getModifiers()) || 
      !Modifier.isPublic(field.getDeclaringClass().getModifiers())) 
     { 
      field.setAccessible(true); 
     } 
    } 
} 

public class Application { 
    public static void main(String[] args) throws Exception { 
     KalaGameState obj = new KalaGameState(); 
     Field field = ReflectionUtil.getField(obj.getClass(), 'turn'); 
     ReflectionUtil.makeAccessible(field); 
     field.setInt(obj, 666); 
     System.out.println("turn is " + field.get(obj)); 
    } 
} 
1

No quería arrastrar en más bibliotecas, así que hice una que funcionaba para mí. Es una extensión de uno de los métodos de jweyrich:

import java.lang.reflect.Field; 
import java.lang.reflect.Modifier; 
import java.util.Date; 
import java.util.Random; 
import java.util.UUID; 

public abstract class POJOFiller { 

    static final Random random = new Random(); 

    public static void fillObject(Object ob) { 
     Class<? extends Object> clazz = ob.getClass(); 

     do { 
      Field[] fields = clazz.getDeclaredFields(); 
      fillForFields(ob, fields); 

      if (clazz.getSuperclass() == null) { 
       return; 
      } 
      clazz = clazz.getSuperclass(); 

     } while (true); 

    } 

    private static void fillForFields(Object ob, Field[] fields) { 
     for (Field field : fields) { 
      field.setAccessible(true); 

      if(Modifier.isFinal(field.getModifiers())) { 
       continue; 
      } 

      try { 
       field.set(ob, generateRandomValue(field.getType())); 
      } catch (IllegalArgumentException | IllegalAccessException e) { 
       throw new IllegalStateException(e); 
      } 
     } 
    } 

    static Object generateRandomValue(Class<?> fieldType) { 
     if (fieldType.equals(String.class)) { 
      return UUID.randomUUID().toString(); 
     } else if (Date.class.isAssignableFrom(fieldType)) { 
      return new Date(System.currentTimeMillis()); 
     } else if (Number.class.isAssignableFrom(fieldType)) { 
      return random.nextInt(Byte.MAX_VALUE) + 1; 
     } else if (fieldType.equals(Integer.TYPE)) { 
      return random.nextInt(); 
     } else if (fieldType.equals(Long.TYPE)) { 
      return random.nextInt(); 
     } else if (Enum.class.isAssignableFrom(fieldType)) { 
      Object[] enumValues = fieldType.getEnumConstants(); 
      return enumValues[random.nextInt(enumValues.length)]; 
     } else if(fieldType.equals(Integer[].class)) { 
      return new Integer[] {random.nextInt(), random.nextInt()}; 
     } 
     else { 
      throw new IllegalArgumentException("Cannot generate for " + fieldType); 
     } 
    } 

} 
0

Un método utilidad genérica para ejecutar cualquier getter en este o cualquier superclase.

Adaptado de Marius's respuesta.

public static Object RunGetter(String fieldname, Object o){ 
    Object result = null; 
    boolean found = false; 
    //Search this and all superclasses: 
    for (Class<?> clas = o.getClass(); clas != null; clas = clas.getSuperclass()){ 
     if(found){ 
      break; 
     } 
     //Find the correct method: 
     for (Method method : clas.getDeclaredMethods()){ 
      if(found){ 
       break; 
      } 
      //Method found: 
      if ((method.getName().startsWith("get")) && (method.getName().length() == (fieldname.length() + 3))){ 
       if (method.getName().toLowerCase().endsWith(fieldname.toLowerCase())){        
        try{ 
         result = method.invoke(o); //Invoke Getter: 
         found = true; 
        } catch (IllegalAccessException | InvocationTargetException ex){ 
         Logger.getLogger("").log(Level.SEVERE, "Could not determine method: " + method.getName(), ex); 
        } 
       } 
      } 
     } 
    } 
    return result; 
} 

Espero que sea útil para alguien.

1

Si se acaba de entrar el campo de protección

Field protectedfield = Myclazz.class.getSuperclass().getDeclaredField("num"); 

Si está utilizando Eclipse Ctrl +espacio aparecerá una lista de métodos cuando se escribe un "" después del objeto

3

Utilice FieldUtils.writeField(object, "fieldname", value, true) o readField(object, "fieldname", true) desde Apache Commons lang3.

1

Si se utiliza la primavera, ReflectionTestUtils proporciona algunas herramientas útiles que ayudan a cabo aquí con un mínimo esfuerzo.

Por ejemplo, para obtener un valor de campo protegida que se sabe que es un int:

int theIntValue = (int)ReflectionTestUtils.getField(theClass, "theProtectedIntField"); 

O, alternativamente, para establecer el valor de este campo:

ReflectionTestUtils.setField(theClass, "theProtectedIntField", theIntValue); 
Cuestiones relacionadas