使用Reflection调用公共方法

时间:2015-11-22 01:32:48

标签: java android reflection

我正在编写API,但我遇到了使用反射的问题。我想要做的是从自定义对象类调用一个方法,并将值返回到被调用的点。调用的开始发生在MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ByteMe b = new ByteMe();

        ExampleObject object = new ExampleObject("Bob", 20, "indy", "male", "its bobby");
        try {
            b.examine(object);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

我的示例对象类旨在帮助确保此API正常工作

public class ExampleObject {

    private String Name;
    private int Age;
    private String Location;
    private String Sex;
    private String Description;

    /**
     * Empty Constructor
     */
    public ExampleObject() {}

    /**
     * Basic constructor with initializing data
     *
     * @param _Name String with the name of the user
     * @param _Age Integer with the age of the user
     * @param _Location String containing the curret city and state of the user
     * @param _Sex String Male, Female, Transgender, or Other
     * @param _Description String short blurb about the user
     */
    public ExampleObject(String _Name, int _Age, String _Location, String _Sex, String _Description)
    {
        this.setName(_Name);
        this.setAge(_Age);
        this.setLocation(_Location);
        this.setSex(_Sex);
        this.setDescription(_Description);
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public int getAge() {
        return Age;
    }

    public void setAge(int age) {
        Age = age;
    }

    public String getLocation() {
        return Location;
    }

    public void setLocation(String location) {
        Location = location;
    }

    public String getSex() {
        return Sex;
    }

    public void setSex(String sex) {
        Sex = sex;
    }

    public String getDescription() {
        return Description;
    }

    public void setDescription(String description) {
        Description = description;
    }
}

现在我遇到的主要问题是当我在另一个名为ByteMe.java的文件中进行此调用时:

public void examine(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        //Get the list of possible methods held in this object.
        Method[] methods = obj.getClass().getMethods();

        // iterate through them
        for (Method method : methods) {
            Log.d("" + this.getClass().getName(), "--------------------------");
            Log.d("" + this.getClass().getName(), "Method: " + method.getName());
            Log.d("" + this.getClass().getName(), "Return Type: " + method.getReturnType());
            Log.d("" + this.getClass().getName(), "Class: " + method.getClass());
            Log.d("" + this.getClass().getName(), "Declaring Class: " + method.getDeclaringClass());

            if(method.getReturnType().getName().contains("int")) {
                try {
                    Method m = method.getDeclaringClass().getMethod(method.getName(), Integer.TYPE);
                    int temp = (int) m.invoke(null, 0); //first argument is the object to invoke on, ignored if static method
                    Log.d("" + this.getClass().getName(),"temp value: " + temp);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

            Log.d("" + this.getClass().getName(), "--------------------------");
        }
    }

这是我的logcat的摘录:

11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: --------------------------
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Method: equals
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Return Type: boolean
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Class: class java.lang.reflect.Method
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Declaring Class: class java.lang.Object
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: --------------------------
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: --------------------------
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Method: getAge
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Return Type: int
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Class: class java.lang.reflect.Method
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: Declaring Class: class productions.widowmaker110.byteme.ExampleObject
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err: java.lang.NoSuchMethodException: getAge [int]
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.Class.getMethod(Class.java:664)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.Class.getMethod(Class.java:643)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at Library.ByteMe.examine(ByteMe.java:94)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at productions.widowmaker110.byteme.MainActivity.onCreate(MainActivity.java:21)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.Activity.performCreate(Activity.java:5958)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2474)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.access$800(ActivityThread.java:144)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1359)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.os.Looper.loop(Looper.java:155)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5696)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: --------------------------

我查找了NoSuchMethodFound错误,大多数stackoverflow表示这是由于方法为private但我的方法是public。 examine()中的invoke方法应该返回20,因为那是我在MainActivity中创建的对象的年龄。它只是抛出异常。帮助

编辑 Alain O' Dea,感谢您的帮助。这就是我必须改变以使其发挥作用。

 public void examine(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        //Get the list of possible methods held in this object.
        Method[] methods = obj.getClass().getMethods();

        // iterate through them
        for (Method method : methods) {
            Log.d("" + this.getClass().getName(), "--------------------------");
            Log.d("" + this.getClass().getName(), "Method: " + method.getName());
            Log.d("" + this.getClass().getName(), "Return Type: " + method.getReturnType());
            Log.d("" + this.getClass().getName(), "Class: " + method.getClass());
            Log.d("" + this.getClass().getName(), "Declaring Class: " + method.getDeclaringClass());

            if(method.getReturnType().getName().contains("int")) {
                try {
                    int temp = (int) method.invoke(obj);
                    Log.d("" + this.getClass().getName(),"temp value: " + temp);
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

            Log.d("" + this.getClass().getName(), "--------------------------");
        }
    }

logcat的

11-21 20:48:15.459 26949-26949/productions.widowmaker110.byteme D/Library.ByteMe: --------------------------
11-21 20:48:15.469 26949-26949/productions.widowmaker110.byteme D/Library.ByteMe: Method: getAge
11-21 20:48:15.469 26949-26949/productions.widowmaker110.byteme D/Library.ByteMe: Return Type: int
11-21 20:48:15.469 26949-26949/productions.widowmaker110.byteme D/Library.ByteMe: Class: class java.lang.reflect.Method
11-21 20:48:15.469 26949-26949/productions.widowmaker110.byteme D/Library.ByteMe: Declaring Class: class productions.widowmaker110.byteme.ExampleObject
11-21 20:48:15.469 26949-26949/productions.widowmaker110.byteme D/Library.ByteMe: temp value: 20
11-21 20:48:15.469 26949-26949/productions.widowmaker110.byteme D/Library.ByteMe: --------------------------

2 个答案:

答案 0 :(得分:3)

您有以下代码:

int temp = (int) m.invoke(null, 0); //first argument is the object to invoke on, ignored if static method

结果你得到了这个堆栈跟踪:

11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err: java.lang.NoSuchMethodException: getAge [int]
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.Class.getMethod(Class.java:664)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.Class.getMethod(Class.java:643)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at Library.ByteMe.examine(ByteMe.java:94)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at productions.widowmaker110.byteme.MainActivity.onCreate(MainActivity.java:21)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.Activity.performCreate(Activity.java:5958)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1129)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2474)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.access$800(ActivityThread.java:144)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1359)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.os.Looper.loop(Looper.java:155)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5696)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
11-21 20:08:11.704 18121-18121/productions.widowmaker110.byteme D/Library.ByteMe: --------------------------

值得注意的是 getAge(int)是一种非静态(实例)方法:

public int getAge() {
    return Age;
}

触发该异常的问题源于此代码:

Method m = method.getDeclaringClass().getMethod(method.getName(), Integer.TYPE);

这会要求一个方法 int methodName (例如 int getAge(int)),它不存在触发 NoSuchMethodException 。这是一个非常有效的步骤,因为您可以在方法上调用调用

由于调用也存在问题,因此不会单独工作。因此,您必须使用对象进行反射调用,而不是 null 。这也不是完整的问题。方法是 int getAge()而不是 int getAge(int)。它不需要 int 参数。因此,您还需要从调用中删除 0 参数。

以下是更正后的行:

int temp = (int) method.invoke(obj); //first argument is the object to invoke on, ignored if static method

Object Method.invoke(Object,Object ...)的文档可供参考:

public Object invoke(Object obj,
            Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException
     

在on上调用此Method对象表示的基础方法   具有指定参数的指定对象。个人   参数自动解包以匹配原始形式   参数,以及原始参数和参考参数都受制于   必要时进行方法调用转换。

     

如果底层方法是静态的,那么指定的obj参数是   忽略。它可能为空。

     

如果底层方法所需的形式参数的数量   如果为0,则提供的args数组的长度可以为0或null。

     

如果底层方法是实例方法,则使用它调用它   Java语言中记录的动态方法查找   规范,第二版,第15.12.4.4节;特别是,   将发生基于目标对象的运行时类型的覆盖。

     

如果底层方法是静态的,则声明该方法的类   如果尚未初始化,则进行初始化。

     

如果方法正常完成,则返回它返回的值   调用的调用者;如果值具有基本类型,则它是第一个   适当地包裹在一个物体中。但是,如果值具有类型   对于基本类型的数组,数组的元素不是   包裹在物体中;换句话说,一个原始类型的数组是   回。如果底层方法返回类型为void,则调用   返回null。

     

参数:

     
      
  • obj - 从
  • 调用基础方法的对象   
  • args - 用于方法调用的参数
  •   
     

返回:

     
      
  • 调度由此表示的方法的结果   obj上的对象参数args
  •   
     

抛出:

     
      
  • IllegalAccessException - 如果此Method对象正在执行   Java语言访问控制和底层方法是   不可访问。
  •   
  • IllegalArgumentException - 如果方法是   instance方法和指定的object参数不是实例   类或接口声明底层方法(或一个   子类或其实现者);如果实际和正式的数量   参数不同;如果是原始参数的解包转换   失败;或者,如果在可能的展开后,参数值不能   通过方法转换为相应的形式参数类型   调用转换。
  •   
  • InvocationTargetException - 如果是   底层方法抛出异常。
  •   
  • NullPointerException - 如果   指定的对象为null,方法是实例方法。   ExceptionInInitializerError - 如果由此引发的初始化   方法失败。
  •   

消息来源:http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object,%20java.lang.Object...)

答案 1 :(得分:2)

如果查看Class类的docs,您会看到getMethod(String name, Class...<?> parameterTypes)接受表示方法名称的参数(在您的情况下为“getAge”)和参数类型(Integer) 。您正在访问的类没有带该签名的方法(public int getAge(int age)),因此NoSuchMethodException。这不是骗你的。

此外,由于您已经有了逐步执行的方法,为什么不调用m.invoke(obj)如果它是正确的,而不是再次查询该方法的类?这可能会让事情变得清醒一些。

最后,正如你的评论所说,当你调用invoke()时,你需要传递你正在调用该方法的对象,除非它是静态的(它不是)。因此,您需要将其更改为m.invoke(obj)