Любой способ вызвать частный метод?

У меня есть класс, который использует XML и отражение для возврата Objects другому классу.

Обычно эти объекты являются подполями внешнего объекта, но иногда это что-то, что я хочу сгенерировать на лету. Я пробовал что-то подобное, но безуспешно. Я считаю, что это потому, что Java не позволит вам получить доступ к private методам отражения.

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

Если предоставлен метод private, он не работает с NoSuchMethodException. Я мог бы решить эту проблему, создав метод public или создав другой класс для его производных.

Короче говоря, мне просто было интересно, есть ли способ получить доступ к private методу через отражение.


person Sheldon Ross    schedule 19.05.2009    source источник


Ответы (7)


Вы можете вызвать частный метод с отражением. Изменение последнего бита опубликованного кода:

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

Есть пара предостережений. Во-первых, getDeclaredMethod найдет только метод, объявленный в текущем Class, не унаследованный от супертипов. Итак, при необходимости пройдитесь по иерархии конкретных классов. Во-вторых, SecurityManager может предотвратить использование метода setAccessible. Таким образом, возможно, потребуется запустить его как PrivilegedAction (используя AccessController или Subject).

person erickson    schedule 19.05.2009
comment
когда я делал это раньше, я также вызывал method.setAccessible (false) после вызова метода, но я понятия не имею, нужно это или нет. - person shsteimer; 19.05.2009
comment
Нет, когда вы устанавливаете доступность, она применяется только к этому экземпляру. Пока вы не позволяете этому конкретному объекту Method выйти из-под вашего контроля, это безопасно. - person erickson; 19.05.2009
comment
@erickson у меня есть один вопрос, разрешает ли java 1.1 доступ к частным методам с использованием API отражения .... Я прочитал по этой ссылке, что это не junit.sourceforge.net/doc/cookstour/cookstour.htm API отражения JDK 1.1 позволяет нам находить только общедоступные методы - person Anil Sharma; 04.04.2013
comment
Итак, какой смысл иметь частные методы, если их можно вызывать извне класса? - person Peter Ajtai; 20.09.2013
comment
Думаю, это дополняет ваш ответ относительно AccessController - person Aquarius Power; 08.12.2015
comment
Также убедитесь, что вы вызываете getDeclaredMethod(), а не просто getMethod() - это не сработает для частных методов. - person Ercksen; 14.12.2015
comment
@PeterAjtai Извините за запоздалый ответ, но подумайте об этом так: в настоящее время большинство людей запирают свои двери, даже если они знают, что замок можно тривиально взломать или вообще обойти. Почему? Потому что это помогает сохранять честность в большинстве своем честных людей. Вы можете представить себе private доступ, играющий аналогичную роль. - person erickson; 04.02.2018

Используйте getDeclaredMethod(), чтобы получить частный объект Method, а затем используйте method.setAccessible(), чтобы разрешить его фактически вызвать.

person Mihai Toader    schedule 19.05.2009
comment
В моем собственном примере (stackoverflow.com/a/15612040/257233) я получаю java.lang.StackOverflowError, если не вызываю setAccessible(true). - person Robert Mark Bram; 25.03.2013

Если метод принимает непримитивные типы данных, то для вызова частного метода любого класса можно использовать следующий метод:

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

Допустимые параметры: obj, methodName и параметры. Например

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

Метод concatString можно вызвать как

Test t = new Test();
    String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");
person gKaur    schedule 20.09.2013
comment
Зачем нужен paramCount? Разве вы не можете просто использовать params.length? - person Saad Malik; 22.04.2014

вы можете сделать это с помощью ReflectionTestUtils of Spring (org.springframework.test.util.ReflectionTestUtils)

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

Пример: если у вас есть класс с частным методом square(int x)

Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
person KaderLAB    schedule 14.05.2018
comment
Существует также более ограниченный ReflectionUtils в ядре Spring. - person Thunderforge; 24.08.2018

Позвольте мне предоставить полный код для выполнения защищенных методов через отражение. Он поддерживает любые типы параметров, включая общие, автоматически запакованные параметры и нулевые значения.

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

Метод использует apache ClassUtils для проверки совместимости параметров autoboxed

person Pavel Chertalev    schedule 05.04.2018
comment
Нет абсолютно никакого смысла отвечать на вопрос 9-летней давности, набравший более 90000 просмотров и принятый ответ. Вместо этого ответьте на неотвеченные вопросы. - person Anuraag Baishya; 05.04.2018

Еще один вариант - использование очень мощной библиотеки JOOR https://github.com/jOOQ/jOOR.

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

Он позволяет изменять любые поля, такие как конечные статические константы, и вызывать защищенные методы без указания конкретного класса в иерархии наследования.

<!-- 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>
person Pavel Chertalev    schedule 05.04.2018

Вы можете использовать Manifold @Jailbreak для прямой типобезопасной Java отражение:

@Jailbreak Foo foo = new Foo();
foo.callMe();

public class Foo {
    private void callMe();
}

@Jailbreak разблокирует foo локальную переменную в компиляторе для прямого доступа ко всем элементам в иерархии Foo.

Точно так же вы можете использовать метод расширения jailbreak () для одноразового использования:

foo.jailbreak().callMe();

С помощью метода jailbreak() вы можете получить доступ к любому члену в иерархии Foo.

В обоих случаях компилятор разрешает вызов метода для вас с безопасным типом, как если бы это был общедоступный метод, в то время как Manifold генерирует эффективный код отражения для вас под капотом.

В качестве альтернативы, если тип неизвестен статически, вы можете использовать структурную типизацию для определения интерфейс, которому тип может удовлетворить, не объявляя его реализацию. Эта стратегия поддерживает безопасность типов и позволяет избежать проблем с производительностью и идентификацией, связанных с кодом отражения и прокси.

Узнайте больше о Manifold.

person Scott    schedule 09.12.2018