【问题标题】:How to call a method by name (String) in Java?如何在 Java 中按名称(字符串)调用方法?
【发布时间】:2013-09-10 20:57:39
【问题描述】:

A 类的每个实例都有B 类的实例。 A 应该根据其成员变量method_numB 中调用不同的方法。这是一个实现我想要的实现:

public class A {
    private B myB = new B();
    public int method_num = 1;
    public callBMethod() {
        if ( method_num == 1 )
            myB.method1();
        else
            myB.method2();
    }
}

public class B {
    public method1() { }
    public method2() { }
}

但我不想做myA.method_num = 1,而是希望能够以某种方式直接传递B的method1method2。我该怎么做?

【问题讨论】:

  • 阅读 Java 反射。
  • 我想传递方法名不是问题,所以我改变了问题的标题。
  • 反思,虽然不是特别直接。
  • @larsmans 除非有某种方法可以传递“方法对象”。
  • @Andreas:不幸的是,没有这样的事情。但在任何情况下,您都不会传递 name

标签: java


【解决方案1】:

如果您不想使用反射(这是一个很好的目标),那么 enums 的一些简洁功能允许您将 enum 设置为代理。

public class A {
  private B myB = new B();
  public int method_num = 1;

  public void callBMethod() {
    // Could do it by name.
    BMethods.valueOf("method1").call(myB);
    // Or by number.
    BMethods.values()[method_num].call(myB);
  }

}

enum BMethods{
  method1 {
    @Override
    public void call(B b) {
      b.method1();
    }
  },
  method2 {
    @Override
    public void call(B b) {
      b.method2();
    }
  };

  public abstract void call (B b);
}

public class B {
  public void method1() {
  }

  public void method2() {
  }

}

【讨论】:

  • @Woot4Moo - er ... 对于 B 类中的每个方法,您需要一个代理它的 enum。我承认随着 B 中方法数量的增加,这会变得很麻烦,但它避免了反思,这对我来说是一个值得称赞的收获。
  • 当然同意。我会将它视为两个模块之间的另一个“接口”。只是想为未来的读者提一下缩放部分。
  • 同理 - 使用这种方法仍然可以追踪B 的每种方法的所有用户,并绝对确保您拥有所有用户。一旦你在代码中的任何地方使用反射——就不再可能了。
【解决方案2】:

你不能。 Java 不将函数视为第一类对象,因为它没有 Python 或 C# 之类的函数特性。

您可以创建一个 Command 接口并传递该对象引用:

public interface Command {
    void execute(Object [] args);
}

【讨论】:

  • 谢天谢地,这将在 Java 8 中发生变化。
  • 你可以通过反射来做到这一点。不过有点乱。
  • @arshajii -- “谢天谢地”?? (颤抖!)
  • 我认为添加诸如 C# 委托之类的东西会是一件好事。为什么不寒而栗?
  • @HotLicks 你是什么意思?
【解决方案3】:

我认为你可以像this这样使用反射:

java.lang.reflect.Method method;
try {
  method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) {
  // ...
} catch (NoSuchMethodException e) {
  // ...
}  

try {
  method.invoke(obj, arg1, arg2,...);
} catch (IllegalArgumentException e) {  //do proper handling
} catch (IllegalAccessException e) {//do proper handling
} catch (InvocationTargetException e) {//do proper handling

【讨论】:

  • 我不会向任何人推荐空的 catch 块。
  • @duffymo 我也不会,这是来自链接答案的直接端口:)
  • 我不会让错误的答案永远存在。
【解决方案4】:

也许有Runnable 对象?你可以从 B 传递一个 runnable,然后直接从 A 调用 .run()

【讨论】:

  • 或许将其用作评论会更好?
  • @Woot4Moo:虽然我同意这是一个简短(太短?)的答案,但我认为使用可运行对象既合适又不在现有答案中。
  • 是的,同意它不在其他答案中,但它没有像其他答案一样提供基于代码的解决方案:)
【解决方案5】:

Java 反射 API 为您提供了一种方法,可以将 Method 类型的对象与目标对象一起传递,然后可以在目标对象上调用该方法。

这里有一个示例:

Method m; // The method to be invoked

  Object target; // The object to invoke it on

  Object[] args; // The arguments to pass to the method

  // An empty array; used for methods with no arguments at all.
  static final Object[] nullargs = new Object[] {};

  /** This constructor creates a Command object for a no-arg method */
  public Command(Object target, Method m) {
    this(target, m, nullargs);
  }

  /**
   * This constructor creates a Command object for a method that takes the
   * specified array of arguments. Note that the parse() method provides
   * another way to create a Command object
   */
  public Command(Object target, Method m, Object[] args) {
    this.target = target;
    this.m = m;
    this.args = args;
  }

  /**
   * Invoke the Command by calling the method on its target, and passing the
   * arguments. See also actionPerformed() which does not throw the checked
   * exceptions that this method does.
   */
  public void invoke() throws IllegalAccessException,
      InvocationTargetException {
    m.invoke(target, args); // Use reflection to invoke the method
  }

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2011-02-12
    • 2010-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多