动态调用非静态方法

4

我有很多相互替换的函数,它们接受一个输入并返回一个输出,但在每种情况下,输出略有不同。所有这些函数都嵌入在单个包装类中。

我想通过将方法名称作为字符串传递来调用它们。我还想避免手动维护一个庞大的不需要的if语句列表:

if(methodName.equals("method1")
    return method1(argument);
...
else if(methodName.equals("method176")
    return method176(argument);

到目前为止,我已经弄清楚了如何动态调用方法,但仅限于静态方法:

try {
    method = MyMainClass.class.getDeclaredMethod(methodName, MyArgumentClass.class);
    ResultClass result = (ResultClass)method.invoke(null, myArgument);
    return result;
}
catch(Exception e)
{
    e.printStackTrace();
    System.exit(1);
    return null;
}

这对非静态方法是否可行?可以在类实例上调用函数,而不是静态类吗?

1
不是答案,但请注意:虽然使用反射在技术上是可能的,但这可能不是一个好主意。您会失去编译类型检查,代码分析(如IDE中的调用层次结构)变得更加困难,并且重命名方法需要更改字符串。我建议您考虑替代方法,例如策略模式或减少方法数量并将它们参数化。 - sleske
5个回答

5

是的。使用Java反射,只需将要调用的实例作为第一个参数传递给invoke方法即可。

阅读:http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html#invoke

try {
    MyArgumentClass argument = new MyArgumentClass();
    MyMainClass instance = new MyMainClass(argument);
    method = MyMainClass.class.getDeclaredMethod(methodName, MyArgumentClass.class);
    ResultClass result = (ResultClass) method.invoke(instance, myArgument);
    return result;
}
catch(Exception e)
{
    e.printStackTrace();
    System.exit(1);
    return null;
}

正如其他答案中指出的那样,这不是解决您问题的最佳方法。您应该像这样实现策略模式

interface MyOperation{
   public ResultClass execute(MyArgumentClass argument);
}

public class Method1 implements MyOperation {
    public ResultClass execute(MyArgumentClass argument){
      ....
    }
}

public class Method176 implements MyOperation {
    public ResultClass execute(MyArgumentClass argument){
      ....
    }
}

public class DoIt{
    public ResultClass doIt(MyOperation method, MyArgumentClass argument){
       return method.doIt(argument);
    }
}

5

尽可能避免使用反射。看起来您的问题可以通过更好地使用策略模式进行设计来解决。


+1 虽然使用反射技术在技术上是可行的,但这可能不是一个好主意。 - sleske
根据维基百科的例子,这似乎并没有太大改变。由于我想将“策略”作为字符串参数传递,因此最终仍然会得到一个巨大的if语句列表:如果(strategy.equals("add")),则上下文= new Context(new ConcreteStrategyAdd()); 如果(strategy.equals("subtract")),则上下文= new Context(new ConcreteStrategySubtract()); ... - Marek
你可以使用Map<String, Strategy>来实现这个。 - Hauke Ingmar Schmidt
或者,你是如何获取这个“String”的?我几乎想建议你应该从一个“enum”中进行选择。 - Louis Wasserman

1

是的,这是可能的。将您想要调用方法的对象作为第一个参数传递给invoke调用。在对象上调用getClass以获取要使用的类。


1
获取您的方法实例,就像您正在执行的类一样检索它,然后使用您希望调用该方法的类的实例调用 Method.invoke()
如果您有兴趣走这条路,您也可以使用 Spring 中提供的一些 bean 来完成类似的操作。

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接