web-dev-qa-db-ja.com

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

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メソッドにアクセスする方法があるのか​​と思っていました。

130
Sheldon Ross

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

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

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

272
erickson

getDeclaredMethod()を使用してプライベートメソッドオブジェクトを取得し、method.setAccessible()を使用して実際に呼び出すことを許可します。

33
Mihai Toader

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

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

springのReflectionTestUtilsを使用してこれを行うことができます( org.springframework.test.util.ReflectionTestUtils

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

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

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

リフレクションを介して実行保護されたメソッドの完全なコードを提供させてください。ジェネリック、オートボックス化されたパラメーター、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を使用して、自動ボックス化されたパラメーターの互換性をチェックします

3
Pavel Chertalev

もう1つのバリエーションは、非常に強力なJOORライブラリを使用することです https://github.com/jOOQ/jOOR

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

最終的な静的定数などのフィールドを変更し、継承階層で具体的なクラスを指定せずにyneで保護されたメソッドを呼び出すことができます。

<!-- 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
Pavel Chertalev

Manifold's@ Jailbreakを使用して、直接のタイプセーフなJavaリフレクションを使用できます。

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

public class Foo {
    private void callMe();
}

@Jailbreakは、fooの階層内のすべてのメンバーに直接アクセスするために、コンパイラーでFooローカル変数のロックを解除します。

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

foo.jailbreak().callMe();

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

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

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

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

0
Scott McKinney