[英]Pass derived object into method wanting superclass using java reflection?
編輯:我不清楚。 我必須使用反射,因為我從命令行解釋。 我正在做的反射相當於我提供的代碼示例。
希望這不是重復,因為它似乎是日常想做的事情。
我有一個A類和一個擴展A的B類。如果我在C類中有一個方法,如public void doSomething(A a),我怎么能用反射將B對象傳遞給這個函數? 我想做(反射)相當於:
B b = new B(); //B inherits from A
C c = new C();
c.doSomething(b); // method signature is doSomething(A a);
我所做的(使用反射)是:
如果我要將A對象傳遞給C.doSomething(...),這很有用。 但是,如果我嘗試將B對象傳遞給C.doSomething(...),則在步驟3中失敗,出現此錯誤:
java.lang.NoSuchMethodException:C.doSomething(B)
讓C.doSomething認識到B是A的合適方法是什么? (使用getDeclaredMethod(String name,Class ... parameterTypes)查找方法並將B.class作為參數類型傳遞時)
編輯:
我會發布我自己的解決方案,以防有人想看到一個快速入侵的方式做Roland Illig所建議的。 在這個例子中,我引用了這些預先制作的變量:
String methodToken; //the name of the method
Object obj; //the object whose method we are trying to call
Object[] args; //the user given arguments for the method
Class[] argTypes; //the types of the args gotten by args[i].getClass();
所以...
//*** try to get the specified method from the object
Method m = null;
// if we are looking for a no-arg version of the method:
if(null == args)
{
try
{
m = obj.getClass().getMethod(methodToken, argTypes);
}
catch ( /*errors*/ )
{
// do stuff
}
}
else // if we are looking for a version of the method that takes arguments
{
// we have to do this type of lookup because our user arguments could be
// subclasses of the arguments required by the method. getMethod will not
// find a match in that case.
try
{
boolean matchFound = false;
Class c = obj.getClass();
do
{ // for each level in the inheritance hierarchy:
// get all the methods with the right name
//(matching the name that the user supplied for the method)
Method[] methodList = c.getMethods();
ArrayList<Method> matchingMethods = new ArrayList<Method>();
for( Method meth : methodList)
{
if(meth.getName().equals(methodToken))
{
matchingMethods.add(meth);
}
}
// check for a matching method signature
for( Method meth : matchingMethods)
{
// get the types of the arguments the method under
// investigation requires.
Class[] paramList = meth.getParameterTypes();
// make sure the signature has the required number of
// elements. If not, this is not the correct method.
if(paramList.length != args.length)
{
continue;
}
// Now check if each method argument is assignable from the
// type given by the user's provided arguments. This means
// that we are checking to see if each of the user's
// arguments is the same as, or is a superclass or
// superinterface of the type found in the method signature
//(i.e. it is legal to pass the user arguments to this
// method.) If one does not match, then this is not the
// correct method and we continue to the next one.
boolean signatureMatch = false;
for ( int i = 0; i < paramList.length; ++i)
{
if(paramList[i].isAssignableFrom( argTypes[i] ) )
{
signatureMatch = true;
}
else
{
continue;
}
}
// if we matched the signature on a matchingly named
// method, then we set the method m, and indicate
// that we have found a match so that we can stop
// marching up the inheritance hierarchy. (i.e. the
// containing loop will terminate.
if(true == signatureMatch)
{
m = meth;
matchFound = true;
break;
}
}
// move up one level in class hierarchy.
c = c.getSuperclass();
}
while(null != c && false == matchFound);
}
catch( /*errors*/)
{
// do stuff
}
}
// check that m got assigned
if(null == m)
{
System.out.println("From DO: unable to match method");
return false;
}
// try to invoke the method !!!!
try
{
m.invoke(obj, args);
}
catch ( /* errors */ )
{
// do stuff
}
希望它能幫助某個人!
您需要遵循Java語言規范第15.12節“方法調用表達式”中概述的相同過程,以查找在編譯時可以找到的相同方法。 簡而言之,它比你想象的要復雜得多。
一個簡單的變體是用正確的名稱檢查所有方法(並且不要忘記所有超類的方法)。 對於這些方法中的每一種,檢查所有參數是否與相應的方法參數分配兼容。 這可能不完美,但在大多數情況下都有效。
[更新:]當一個類中有多個重載方法時,“簡單變體”會失敗。 以下是一些可以使用的示例代碼:
package so7691729;
import static org.junit.Assert.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class MethodCaller {
private boolean isCompatible(Method m, Object... args) {
Class<?>[] parameterTypes = m.getParameterTypes();
if (parameterTypes.length == args.length) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
if (!parameterTypes[i].isAssignableFrom(args[i].getClass())) {
// TODO: make primitive types equivalent to their boxed types.
return false;
}
}
}
} else {
// TODO: maybe handle varargs methods here
return false;
}
return true;
}
public Object call1(String fullyQualifiedMethodName, Object obj, Object... args) throws ClassNotFoundException, IllegalAccessException,
InvocationTargetException {
int lastDot = fullyQualifiedMethodName.lastIndexOf(".");
String className = fullyQualifiedMethodName.substring(0, lastDot);
String methodName = fullyQualifiedMethodName.substring(lastDot + 1);
Class<?> clazz = Class.forName(className);
for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
Set<String> sameNameMethods = Sets.newTreeSet();
Map<String, Method> compatibleMethods = Maps.newTreeMap();
for (Method method : c.getDeclaredMethods()) {
if (method.getName().equals(methodName)) {
sameNameMethods.add(method.toString());
if (isCompatible(method, args)) {
compatibleMethods.put(method.toString(), method);
}
}
}
if (compatibleMethods.size() > 1) {
throw new IllegalArgumentException("Multiple candidates: " + compatibleMethods.keySet());
}
if (compatibleMethods.size() == 1) {
return compatibleMethods.values().iterator().next().invoke(obj, args);
}
if (!sameNameMethods.isEmpty()) {
throw new IllegalArgumentException("Incompatible types for " + sameNameMethods);
}
}
throw new IllegalArgumentException("No method found.");
}
public Object call(String fullyQualifiedMethodName, Object obj, Object... args) {
try {
return call1(fullyQualifiedMethodName, obj, args);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(e);
}
}
public String str(Object obj) {
return "object " + obj;
}
public String str(String str) {
return "string " + str;
}
public int add(int a, int b) {
return a + b;
}
@SuppressWarnings("boxing")
public int addObj(Integer a, Integer b) {
return a + b;
}
private void assertCallingError(String msg, String methodName, Object obj, Object... args) {
try {
call(methodName, obj, args);
fail();
} catch (IllegalArgumentException e) {
assertEquals(msg, e.getMessage());
}
}
@SuppressWarnings("boxing")
@Test
public void test() {
MethodCaller dummy = new MethodCaller();
assertEquals("object 1", call("so7691729.MethodCaller.str", dummy, 1));
assertCallingError("Multiple candidates: " + //
"[public java.lang.String so7691729.MethodCaller.str(java.lang.Object), " + //
"public java.lang.String so7691729.MethodCaller.str(java.lang.String)]", //
"so7691729.MethodCaller.str", dummy, "str");
assertCallingError("Incompatible types for [public int so7691729.MethodCaller.add(int,int)]", "so7691729.MethodCaller.add", dummy, 3, 4);
assertEquals(7, call("so7691729.MethodCaller.addObj", dummy, 3, 4));
assertCallingError("Incompatible types for [public int so7691729.MethodCaller.addObj(java.lang.Integer,java.lang.Integer)]", "so7691729.MethodCaller.addObj", dummy, "hello", "world");
}
}
也許Java Beans規范或實現可以為您提供幫助。 他們可能有同樣的問題需要解決。 或者看看Rhino,一個純Java的JavaScript實現。 它允許您直接從JavaScript代碼調用Java方法,因此這與您的問題非常相似。
3)根據參數的類查找方法
你在問班級:“你有沒有任何方法正好簽名?” 班上寫着“不!” 你不是在問“上課,你能用這些參數調用嗎?” 如前所述,只要涉及繼承和重載方法,就不容易回答,因此完整的Reflection API無法解決此問題。
但是:你不是第一個想要對第二個問題有用答案的人。 也許MethodUtils.invokeMethod
或來自Apache Commons Beanutils項目的任何兄弟都適合您。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.