views:

869

answers:

5

I have a poorly designed class in a 3rd-party JAR and I need to access one of its private fields. For example,

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

How can I use reflection to get the value of stuffIWant?

+26  A: 

In order to access private fields, you need to get them from the class's declared fields and then make them accessible:

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

EDIT: as has been commented by aperkins, both accessing the field, setting it as accessible and retrieving the value will all throw Exceptions, although the only checked exceptions you need to be mindful of are commented above.

The NoSuchFieldException would be thrown if you asked for a field by a name which did not correspond to a declared field.

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

The IllegalAccessException would be thrown if the field was not accessible (for example, if it is private and has not been made accessible via missing out the f.setAccessible(true) line.

The RuntimeExceptions which may be thrown are either SecurityExceptions (if the JVM's SecurityManager will not allow you to change a field's accessibility), or IllegalArgumentExceptions, if you try and access the field on an object not of the field's class's type:

f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
oxbow_lakes
Also note you will have to catch an exception or two off of f.get
aperkins
can you please explain the exception comments? the code will run but will throw exceptions? or the code may throw exceptions?
Nir Levy
@Nir - No - in all likelihood the code will run just fine (as the default SecurityManager will allow fields' accesibility to be changed) - but you have to *handle* checked exceptions (either catch them or declare them to be *rethrown*). I've modified my answer a little. It might be good for you to write a small test case to play around and see what happens
oxbow_lakes
@Nir - exactly what oxbow said. The code will have checked exceptions around getting the declared field, and getting the value off of the object. You just need to make sure to handle those cases.
aperkins
+2  A: 

As oxbow_lakes mentions, you can use reflection to get around the access restrictions (assuming your SecurityManager will let you).

That said, if this class is so badly designed that it makes you resort to such hackery, maybe you should look for an alternative. Sure this little hack might be saving you a few hours now, but how much will it cost you down the road?

Laurence Gonsalves
I'm luckier than that actually, I'm just using this code to extract some data, after that I can toss it back into the Recycle Bin.
Frank Krueger
Well in that case, hack away. :-)
Laurence Gonsalves
+3  A: 

Reflection isn't the only way.

An alternative solution is to extract the class from the .jar, decompile it using (say) Jode or Jad, change the field (or add an accessor), and recompile it against the original .jar. Then put the new .class ahead of the .jar in the classpath, or reinsert it in the .jar. (the jar utility allows you to extract and reinsert to an existing .jar)

This requires the .jar not to be signed, of course.

Brian Agnew
This way will be pretty painful for just a simple field.
Valentin Rocher
I disagree. It allows you to not only access your field, but also to change the class if necessary if accessing the field turns out not to be sufficient.
Brian Agnew
+1  A: 

Use the Soot Java Optimization framework to directly modify the bytecode. http://www.sable.mcgill.ca/soot/

Soot is completely written in Java and works with new Java versions.

Phil Pratt-Szeliga
+1  A: 

One other option that hasn't been mentioned yet: use Groovy. Groovy allows you to access private instance variables as a side affect of the design of the language. Whether or not you have a getter for the field, you can just use

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