【问题标题】:Java reflection: automatically invoke the right overloaded method based on parameters and signatureJava反射:根据参数和签名自动调用正确的重载方法
【发布时间】:2017-08-09 07:37:26
【问题描述】:

假设有一个名为doTask 的重载方法:

public class Game {
  void doTask(Joker joker);
  void doTask(Batman batman, Robin robin);
}

我想调用正确的方法,给定方法的名称 ("doTask") 和一个参数数组,其数量和类型是先验未知的。

通常,这至少涉及三个步骤:
1.求参数个数及其类型,组成数组Class[] myTypes
2.识别正确的重载Method,即Method rightMethod = game.getClass().getMethod("doTask", myTypes);
3.调用方法:rightMethod.invoke(paramArray)

是否存在一种工具可以让 Java 反射自动识别要使用的正确重载方法,从而使我们免于执行步骤 1 和 2?我认为理想情况是:
Library.invoke("doTask", paramArray);

【问题讨论】:

  • 这听起来有点不对劲。你想在这里达到什么目的?为什么不知道参数的数量?你从哪里得到它们?它们有什么形式?当你不知道参数的数量时,你怎么能确定有一个具有正确数量参数的实现呢?是否可以将参数列表直接传递给doTask() 并让实现决定如何处理它们?
  • 重载:带有一些最小的设计气味。反射:更大的气味。长话短说:您打算在这里解决的根本问题是什么?
  • lupz 和 GhostCat,我同意你的观点,如果我从头开始编写整个项目,就不会这样写。就目前而言,doTask() 方法已经被编写(并被其他代码使用)。我正在编写一个包装函数myFunc(Object... paramArray),带有额外的业务逻辑,以调用正确的doTask()
  • 另一种选择当然是为每个重载的doTask() 方法编写一个单独的包装器。但这会重复大部分业务逻辑代码,并且难以维护。
  • 这个business logic 对所有参数都一样吗?如果否,那么您已经可以识别应该使用一个包装方法调用哪个doTask()

标签: java reflection overloading


【解决方案1】:

有这样的设施,java.beans.Statement,分别。 Expression 如果需要返回值:

Game game = new Game();
Joker joker = new Joker();
Statement st = new Statement(game, "doTask", new Object[]{ joker });
st.execute();

但是,它仅适用于 public 方法。

另外,与java.lang.reflect.Method 不同的是,此工具尚未适应支持varargs 参数,因此您必须手动创建参数数组。

可以证明,它可以根据参数类型选择正确的目标方法,这些参数类型不一定与参数类型相同:

ExecutorService es = Executors.newSingleThreadExecutor();
class Foo implements Callable<String> {
    public String call() throws Exception {
        return "success";
    }
}
// has to choose between submit(Callable) and submit(Runnable)
// given a Foo instance
Expression ex = new Expression(es, "submit", new Object[]{ new Foo() });
Future<?> f = (Future<?>)ex.getValue();
System.out.println(f.get());
es.shutdown();

【讨论】:

  • 感谢您的详细回答。我最初被“参数类型不一定!=参数类型”难住了,但我认为意思是这样的:new Foo() is of type Foo(参数类型),它与Callable&lt;&gt;(参数类型)。但是由于FooCallable 的子类,所以会调用右边的submit。 @Holger 我说的对吗?
  • 另外,由于 ExpressionStatement 在 beans 包中,它们只适用于 JavaBeans 类,对吧?
  • @Holger 哦该死的......没想到这会存在
  • @flow2k: 对,仅仅调用getMethod("submit", argument.getClass()); 会失败,因为没有submit(Foo) 方法,所以实现必须检查每个方法的适用性,正如 Gerald Mücke 所阐述的那样。值得庆幸的是,这已经实施了。关于“Java Beans”,bean 模式非常广泛,几乎可以匹配每个类。我的回答中已经提到了唯一的限制;它仅适用于 public 方法。我的示例已经证明了这一点,它使用了ExecutorService 和内部类Foo,也没有任何显式操作成为Java Bean。
【解决方案2】:

首先 - 回答您的问题 - 不,没有这样的设施。

其次,第 2 步有点复杂,因为它不足以从参数创建一个类数组并调用getMethod

实际上,您必须遍历与名称、参数数量匹配的所有方法,并比较方法的参数类型以确保给定参数类型的赋值兼容性(即methodArgType.isAssignableFrom(paramType)),以确保兼容的子类型方法参数类型正确反映。使用可变参数会使事情变得稍微复杂一些。

【讨论】:

  • 广为人知,there is such a facility...
  • @Gerald Mücke 感谢您的意见。关于第 2 步和第 3 步中的 cmets,看来您可以简单地执行第 2 步:Class[] argTypeArray = new Class[paramArray.length]; for (int n = 0; n &lt; paramArray.length; n++){ argTypeArray[n]=paramArray[n].getClass(); }
  • Method rightMethod = Game.class.getMethod("doTask",argTypeArray)
  • 最后,对于第 3 步,rightMethod.invoke(game, paramArray)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多