views:

919

answers:

5
+2  Q: 

Java reflection

How can I access an inherited protected field from an object by reflection?

+2  A: 
field = myclass.getDeclaredField("myname");
field.setAccessible(true);
field.set(myinstance, newvalue);
Maurice Perry
This may not work if there is a security manager that blocks the relevant permission.
Miguel Ping
getField(String name) get the public fields only.
Moro
@Moro: thanks, I corrected the code
Maurice Perry
Thanx for try, but as Javadoc (and I tried it) it Returns a Field object that reflects the specified declared field of the class or interface represented by this Class object. it doesn't go through super classes.
Moro
Sorry,it wasn't clear that you didn't new in which class the field was declared
Maurice Perry
A: 

Do you perhaps mean from a different object an untrusted context with a SecurityManager set? That would break the type system, so you can't. From a trusted context, you can call setAccessible to defeat the type system. Ideally, don't use reflection.

Tom Hawtin - tackline
"Ideally, don't use reflection." Why? The OP is specifically trying to use reflection...while he doesn't say why, there are many legitimate uses for reflection (especially in test code).
David Citron
Whilst there are legitimate uses of reflection, most are not. In particular if you are creeping around the legacy ability to respect Java (1.0) language access rules, you probably don't need it.
Tom Hawtin - tackline
@Tom ...unless you're trying to write specific JUnit test cases without loosening access rules simply for the test case
David Citron
Unit testing is "interesting". You could argue that it forces your code to be cleaner (alternatively unnecessarily general). Tests don't necessarily follow the usual rules of nice code.
Tom Hawtin - tackline
yes Tom, It was test case issue
Moro
A: 

You could do something like...

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

You might also need to change the accessibility, as noted in Maurice's answer.

craig
Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object. It didn't go through the super classes
Moro
Could you be more specific with the issue? You're saying it only obtained the fields in your subclass? Have you made sure that the Class.forName() method is being passed the name of the superclass, and that the accessibility has been modified, as Maurice suggested?
craig
+5  A: 

Two issues you may be having issues with - the field might not be accessible normally (private), and its not in the class you are looking at, but somewhere up the hierarchy.

Something like this would work even with those issues:

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);
  }
}
kenj0418
Just change clazz.getDeclaredField("value"); to clazz.getDeclaredField(fieldName); -- just a copy/paste error I'm sure.
David Citron
Fixed - thanks for catching that.
kenj0418
If there is a security manager, this would fail. You need to wrap up the call to setAccessible and getDeclaredField in a PriviledgedAction, and run it through java.security.AccessController.doPrivileged(...)
Varkhan
A: 

Use reflection to access the members of the class instance, make them accessible and set their respective values. Of course you'd have to know the name of each member you want to change, but I guess that won't be a problem.

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