プライベートメソッドを呼び出す方法はありますか?


146

XMLとリフレクションを使用Objectして別のクラスにを返すクラスがあります。

通常、これらのオブジェクトは外部オブジェクトのサブフィールドですが、時々それをオンザフライで生成したいものです。私はこのようなものを試しましたが、役に立ちませんでした。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リフレクションを介してメソッドにアクセスする方法があるのか​​と思っていました。

回答:


303

リフレクションを使用してプライベートメソッドを呼び出すことができます。ポストされたコードの最後のビットを変更します:

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

注意点がいくつかあります。まず、スーパータイプから継承されていないgetDeclaredMethod、現在Classので宣言されているメソッドのみを検索します したがって、必要に応じて具体的なクラス階層をたどります。第二SecurityManagerに、setAccessibleメソッドの使用を防ぐことができます。したがって、PrivilegedActionAccessControllerまたはを使用してSubject)として実行する必要がある場合があります。


2
過去にこれを行ったときは、メソッドを呼び出した後にmethod.setAccessible(false)も呼び出しましたが、これが必要かどうかわかりません。
shsteimer、2009年

16
いいえ、アクセシビリティを設定すると、そのインスタンスにのみ適用されます。その特定のMethodオブジェクトをコントロールからエスケープさせない限り、安全です。
エリクソン2009年

6
それでは、クラスの外部から呼び出すことができる場合、プライベートメソッドがあることの意味は何でしょうか。
Peter Ajtai 2013

2
またgetDeclaredMethod()、ただ呼び出すのではなく、必ず呼び出すようにしてくださいgetMethod()-これはプライベートメソッドでは機能しません。
Ercksen

1
@PeterAjtai応答が遅いのは申し訳ありませんが、このように考えてください。ロックが簡単に壊れたり、完全に回避されたりする可能性があることを知っていても、現在ほとんどの人がドアをロックしています。どうして?それは、大部分が正直な人々を正直に保つのに役立つからです。privateアクセスは同様の役割を果たしていると考えることができます。
エリクソン2018


25

メソッドが非プリミティブデータ型を受け入れる場合、次のメソッドを使用して、任意のクラスのプライベートメソッドを呼び出すことができます。

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

7
paramCountが必要なのはなぜですか?params.lengthだけを使用できませんか?
Saad Malik、

7

これは、SpringのReflectionTestUtils(org.springframework.test.util.ReflectionTestUtils)を使用して実行できます。

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

例:プライベートメソッドを持つクラスがある場合 square(int x)

Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);

Springのコアには、さらに限定されたReflectionUtilsもあります。
Thunderforge

3

リフレクションを介して実行保護メソッドの完全なコードを提供しましょう。ジェネリック、オートボックス化されたパラメーター、null値など、あらゆるタイプのパラメーターをサポートします

@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を使用します


90000回を超える表示と承認された回答を含む9年前の質問に答えてもまったく意味がありません。代わりに未回答の質問に答えてください。
Anuraag Baishya

0

もう1つのバリアントは、非常に強力な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>

0

マニホールドの @Jailbreakを使用して、直接型保証されたJavaリフレクションを実行できます。

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

public class Foo {
    private void callMe();
}

@Jailbreakの階層fooFooのすべてのメンバーに直接アクセスできるように、コンパイラーのローカル変数のロックを解除します。

同様に、jailbreak()拡張メソッドを1回限りの使用に使用できます。

foo.jailbreak().callMe();

jailbreak()メソッドを通じて、Fooの階層内の任意のメンバーにアクセスできます。

どちらの場合も、コンパイラーはパブリックメソッドのように、タイプセーフにメソッド呼び出しを解決しますが、マニフォールドは内部で効率的なリフレクションコードを生成します。

あるいは、型が静的にわからない場合は、構造タイピングを使用して、実装を宣言しなくても、型が満たすことができるインターフェースを定義できます。この戦略は、タイプセーフを維持し、リフレクションとプロキシコードに関連するパフォーマンスとIDの問題を回避します。

マニホールドの詳細をご覧ください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.