使用java反射将派生对象传递给想要超类的方法?

使用java反射将派生对象传递给想要超类的方法?,java,inheritance,reflection,methods,Java,Inheritance,Reflection,Methods,编辑:我不清楚。我必须使用反射,因为我是从命令行进行解释的。我正在做与我提供的代码示例相同的反射 希望这不是一个重复,因为这似乎是一个每天都要做的事情 我有一个类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 sig

编辑:我不清楚。我必须使用反射,因为我是从命令行进行解释的。我正在做与我提供的代码示例相同的反射

希望这不是一个重复,因为这似乎是一个每天都要做的事情

我有一个类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(字符串名称、类…参数类型)查找方法并将B.Class作为参数类型传入时)

    编辑:

    我将发布我自己的解决方案,以防有人想看到一种快速被黑客攻击的方式来实现罗兰·伊利格的建议。在本例中,我引用了这些预先制作的变量:

    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();
    
    所以

    //***尝试从对象获取指定的方法
    方法m=null;
    //如果我们正在寻找该方法的无参数版本:
    if(null==args)
    {
    尝试
    {
    m=obj.getClass().getMethod(methodToken,argTypes);
    }
    捕获(/*错误*/)
    {
    //做事
    }
    }
    else//如果我们正在寻找接受参数的方法的版本
    {
    //我们必须进行这种类型的查找,因为我们的用户参数可能是
    //方法所需的参数的子类。getMethod将不会
    //在这种情况下找到匹配项。
    尝试
    {
    布尔匹配发现=假;
    c类=obj.getClass();
    做
    {//对于继承层次结构中的每个级别:
    //使用正确的名称获取所有方法
    //(匹配用户为方法提供的名称)
    方法[]methodList=c.getMethods();
    ArrayList matchingMethods=新的ArrayList();
    对于(方法方法:方法列表)
    {
    if(meth.getName().equals(methodToken))
    {
    匹配方法。添加(meth);
    }
    }
    //检查匹配的方法签名
    用于(方法方法:匹配方法)
    {
    //获取方法下的参数类型
    //调查需要时间。
    类[]paramList=meth.getParameterTypes();
    //确保签名具有所需的数字
    //元素。如果不是,则这不是正确的方法。
    if(paramList.length!=args.length)
    {
    继续;
    }
    //现在检查每个方法参数是否可以从
    //由用户提供的参数给定的类型。这意味着
    //我们正在检查每个用户的
    //参数与相同,或是超类或
    //在方法签名中找到的类型的superinterface
    //(即,将用户参数传递给此
    //方法。)如果其中一个不匹配,则这不是
    //正确的方法,我们继续下一个。
    布尔符号匹配=假;
    对于(int i=0;i

    希望它有助于某些人!

    您需要遵循Java语言规范第15.12节“方法调用表达式”中概述的相同过程,以找到在编译时可以找到的相同方法。简而言之,这比您想象的要复杂得多

    一个简单的变体是用正确的名称检查所有方法(不要忘记
        //*** 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
        }
    
    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");
      }
    
    }