簡體   English   中英

將派生對象傳遞給想要使用java反射的超類的方法?

[英]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);

我所做的(使用反射)是:

  1. 獲取作為函數參數的對象。
  2. 獲取參數的類
  3. 根據參數的類查找方法。
  4. 調用方法,傳入參數Objects。

如果我要將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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM