XMLとリフレクションを使用してObject
sを別のクラスに返すクラスがあります。
通常、これらのオブジェクトは外部オブジェクトのサブフィールドですが、時々、その場で生成したいものです。私はこのようなことを試みましたが、役に立ちませんでした。それは、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
メソッドにアクセスする方法があるのかと思っていました。
リフレクションを使用してプライベートメソッドを呼び出すことができます。投稿されたコードの最後のビットを変更します。
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);
いくつか注意点があります。まず、getDeclaredMethod
は、現在のClass
で宣言されたメソッドのみを検索し、スーパータイプからは継承しません。そのため、必要に応じて具体的なクラス階層を上に移動します。第二に、SecurityManager
はsetAccessible
メソッドの使用を防ぐことができます。そのため、PrivilegedAction
として実行する必要がある場合があります(AccessController
またはSubject
を使用)。
getDeclaredMethod()
を使用してプライベートメソッドオブジェクトを取得し、method.setAccessible()
を使用して実際に呼び出すことを許可します。
メソッドが非プリミティブデータ型を受け入れる場合、次のメソッドを使用して、任意のクラスのプライベートメソッドを呼び出すことができます。
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");
springのReflectionTestUtilsを使用してこれを行うことができます( org.springframework.test.util.ReflectionTestUtils )
ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);
例:プライベートメソッドsquare(int x)
を持つクラスがある場合
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
リフレクションを介して実行保護されたメソッドの完全なコードを提供させてください。ジェネリック、オートボックス化されたパラメーター、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を使用して、自動ボックス化されたパラメーターの互換性をチェックします
もう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>
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の問題を回避します。
マニホールド の詳細をご覧ください。