174

I have a class that uses XML and reflection to return Objects to another class.

Normally these objects are sub fields of an external object, but occasionally it's something I want to generate on the fly. I've tried something like this but to no avail. I believe that's because Java won't allow you to access private methods for reflection.

Element node = outerNode.item(0);
String methodName = node.getAttribute("method");
String objectName = node.getAttribute("object");

if ("SomeObject".equals(objectName))
    object = someObject;
else
    object = this;

method = object.getClass().getMethod(methodName, (Class[]) null);

If the method provided is private, it fails with a NoSuchMethodException. I could solve it by making the method public, or making another class to derive it from.

Long story short, I was just wondering if there was a way to access a private method via reflection.

Sheldon Ross
  • 5,364
  • 7
  • 31
  • 37

6 Answers6

337

You can invoke private method with reflection. Modifying the last bit of the posted code:

Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);

There are a couple of caveats. First, getDeclaredMethod will only find method declared in the current Class, not inherited from supertypes. So, traverse up the concrete class hierarchy if necessary. Second, a SecurityManager can prevent use of the setAccessible method. So, it may need to run as a PrivilegedAction (using AccessController or Subject).

Aggressor
  • 13,323
  • 24
  • 103
  • 182
erickson
  • 265,237
  • 58
  • 395
  • 493
  • 2
    when I've done this in the past, I've also called method.setAccessible(false) after calling the method, but I have no idea if this is necessary or not. – shsteimer May 19 '09 at 01:53
  • 17
    No, when you set accessibility, it only applies to that instance. As long as you don't let that particular Method object escape from your control, it's safe. – erickson May 19 '09 at 02:58
  • @erickson i have one question does java 1.1 allowed the access of private methods using reflection api....i read at this link that it is not http://junit.sourceforge.net/doc/cookstour/cookstour.htm "The JDK 1.1 reflection API only allows us to find public methods" – Anil Sharma Apr 04 '13 at 10:30
  • 8
    So then what is the point of having private methods if they can be called from outside the class? – Peter Ajtai Sep 19 '13 at 22:56
  • I think this complements your answer concerning [AccessController](http://stackoverflow.com/a/5184335/1422630) – Aquarius Power Dec 07 '15 at 23:41
  • 4
    Also make sure you call `getDeclaredMethod()` instead of just `getMethod()` - this won't work for private methods. – Ercksen Dec 14 '15 at 20:32
  • 8
    @PeterAjtai Sorry for the late response, but think of it this way: Most people nowadays lock their doors, even though they know the lock can be trivially broken or circumvented altogether. Why? Because it helps to keep mostly-honest people honest. You can think of `private` access playing a similar role. – erickson Feb 03 '18 at 21:56
36

Use getDeclaredMethod() to get a private Method object and then use method.setAccessible() to allow to actually call it.

Mihai Toader
  • 12,041
  • 1
  • 29
  • 33
  • In my own example (http://stackoverflow.com/a/15612040/257233) I get a `java.lang.StackOverflowError` if I do not call `setAccessible(true)`. – Robert Mark Bram Mar 25 '13 at 10:11
31

If the method accepts non-primitive data type then the following method can be used to invoke a private method of any class:

public static Object genericInvokeMethod(Object obj, String methodName,
            Object... params) {
        int paramCount = params.length;
        Method method;
        Object requiredObj = null;
        Class<?>[] classArray = new Class<?>[paramCount];
        for (int i = 0; i < paramCount; i++) {
            classArray[i] = params[i].getClass();
        }
        try {
            method = obj.getClass().getDeclaredMethod(methodName, classArray);
            method.setAccessible(true);
            requiredObj = method.invoke(obj, params);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return requiredObj;
    }

The Parameter accepted are obj, methodName and the parameters. For example

public class Test {
private String concatString(String a, String b) {
    return (a+b);
}
}

Method concatString can be invoked as

Test t = new Test();
    String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");
Community
  • 1
  • 1
gKaur
  • 461
  • 4
  • 6
17

you can do this using ReflectionTestUtils of Spring (org.springframework.test.util.ReflectionTestUtils)

ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);

Example : if you have a class with a private method square(int x)

Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
Thunderforge
  • 19,637
  • 18
  • 83
  • 130
KaderLAB
  • 348
  • 3
  • 9
  • 1
    There is also a more limited [ReflectionUtils](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ReflectionUtils.html) in core Spring. – Thunderforge Aug 24 '18 at 20:55
3

Let me provide complete code for execution protected methods via reflection. It supports any types of params including generics, autoboxed params and null values

@SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}

public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass(), instance, methodName, params);
}

@SuppressWarnings("unchecked")
public static <T> T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception {

    Method[] allMethods = clazz.getDeclaredMethods();

    if (allMethods != null && allMethods.length > 0) {

        Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new);

        for (Method method : allMethods) {
            String currentMethodName = method.getName();
            if (!currentMethodName.equals(methodName)) {
                continue;
            }
            Type[] pTypes = method.getParameterTypes();
            if (pTypes.length == paramClasses.length) {
                boolean goodMethod = true;
                int i = 0;
                for (Type pType : pTypes) {
                    if (!ClassUtils.isAssignable(paramClasses[i++], (Class<?>) pType)) {
                        goodMethod = false;
                        break;
                    }
                }
                if (goodMethod) {
                    method.setAccessible(true);
                    return (T) method.invoke(instance, params);
                }
            }
        }

        throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " +
            Arrays.toString(paramClasses));
    }

    throw new MethodNotFoundException("There are no methods found with name " + methodName);
}

Method uses apache ClassUtils for checking compatibility of autoboxed params

  • 1
    There is absolutely no point in answering a 9 year old question with more than 90000 views and an accepted answer. Answer unanswered questions instead. – Anuraag Baishya Apr 05 '18 at 10:49
1

One more variant is using very powerfull JOOR library https://github.com/jOOQ/jOOR

MyObject myObject = new MyObject()
on(myObject).get("privateField");  

It allows to modify any fields like final static constants and call yne protected methods without specifying concrete class in the inheritance hierarhy

<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
     <groupId>org.jooq</groupId>
     <artifactId>joor-java-8</artifactId>
     <version>0.9.7</version>
</dependency>