How to read the value of a private field from a different class in Java?

JavaClassReflectionFieldPrivate

Java Problem Overview


I have a poorly designed class in a 3rd-party JAR and I need to access one of its private fields. For example, why should I need to choose private field is it necessary?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

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

Java Solutions


Solution 1 - Java

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 can 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

Solution 2 - Java

Try FieldUtils from apache commons-lang3:

FieldUtils.readField(object, fieldName, true);

Solution 3 - Java

Reflection isn't the only way to resolve your issue (which is to access the private functionality/behaviour of a class/component)

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)

As noted below, this resolves the wider issue of accessing/changing private state rather than simply accessing/changing a field.

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

Solution 4 - Java

One other option that hasn't been mentioned yet: use Groovy. Groovy allows you to access private instance variables as a side effect 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()

Solution 5 - Java

Using the Reflection in Java you can access all the private/public fields and methods of one class to another .But as per the Oracle documentation in the section drawbacks they recommended that :

"Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform"

here is following code snapts to demonstrate basic concepts of 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(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()));
		}
	}
	
}

Hope it will help.

Solution 6 - Java

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?

Solution 7 - Java

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.

Solution 8 - Java

If using Spring:

In a testing context, ReflectionTestUtils provides some handy tools that can help out here with minimal effort. It's described as being "for use in unit and integration testing scenarios".

In a non-testing context, there is also a similar class named ReflectionUtils but this is described as "Only intended for internal use" - see this answer for a good interpretation of what this means.

To address the example in the original post:

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

Solution 9 - Java

You need to do the following:

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

Solution 10 - Java

Java 9 introduced Variable Handles. You can access a private field of a class using them.

The code for your example will look like following:

var lookup = MethodHandles.lookup();
var handle = MethodHandles
    .privateLookupIn(IWasDesignedPoorly.class, lookup)
    .findVarHandle(IWasDesignedPoorly.class, "stuffIWant", Hashtable.class);
var value = handle.get(obj);

It is also advisable to use Lookup and VarHandle objects as static final fields.

Solution 11 - Java

You can use jOOR for that.

class Foo {
    private final String value = "ABC";
}
class Bar {
    private final Foo foo = new Foo();
    public String value() {
        return org.joor.Reflect
            .on(this.foo)
            .field("value")
            .get();
    }
}
class BarTest {
    @Test
    void accessPrivateField() {
        Assertions.assertEquals(new Bar().value(), "ABC");
    }
}

Solution 12 - Java

Just an additional note about reflection: I have observed in some special cases, when several classes with the same name exist in different packages, that reflection as used in the top answer may fail to pick the correct class from the object. So if you know what is the package.class of the object, then it's better to access its private field values as follows:

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

(This is the example class that was not working for me)

Solution 13 - Java

It is quite easy with the tool XrayInterface. Just define the missing getters/setters, e.g.

interface BetterDesigned {
  Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}

and xray your poor designed project:

IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());

Internally this relies on reflection.

Solution 14 - Java

Try to go around the problem for the case, the calass of which you want to set/get data is one of your own classes.

Just create a public setter(Field f, Object value) and public Object getter(Field f) for that. You can even do some securoty check on your own inside theses member functions. E.g. for the setter:

class myClassName {
    private String aString;

    public set(Field field, Object value) {
        // (A) do some checkings here  for security

        // (B) set the value
        field.set(this, value);
    }
}

Of course, now you have to find out the java.lang.reflect.Field for sString prior to setting of field value.

I do use this technique in a generic ResultSet-to-and-from-model-mapper.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFrank KruegerView Question on Stackoverflow
Solution 1 - Javaoxbow_lakesView Answer on Stackoverflow
Solution 2 - Javayegor256View Answer on Stackoverflow
Solution 3 - JavaBrian AgnewView Answer on Stackoverflow
Solution 4 - JavalucasView Answer on Stackoverflow
Solution 5 - JavaSimmantView Answer on Stackoverflow
Solution 6 - JavaLaurence GonsalvesView Answer on Stackoverflow
Solution 7 - JavapcprattsView Answer on Stackoverflow
Solution 8 - JavaSteve ChambersView Answer on Stackoverflow
Solution 9 - JavaLuke HutchisonView Answer on Stackoverflow
Solution 10 - JavaNolequenView Answer on Stackoverflow
Solution 11 - JavaandreossView Answer on Stackoverflow
Solution 12 - Javaxtof54View Answer on Stackoverflow
Solution 13 - JavaCoronAView Answer on Stackoverflow
Solution 14 - JavakarldegenView Answer on Stackoverflow