【问题标题】:How to link arrays to methods如何将数组链接到方法
【发布时间】:2018-12-31 16:33:00
【问题描述】:

这个问题主要是针对Java的,但如果它适用于所有面向对象的编程语言,那就很好了。

我想知道是否有办法将数组值链接到某个方法。我有一个包含许多方法的程序,当一个巨大的 if 脚本中存在某种条件时,一个方法就会运行。我觉得这很慢而且效率低下。我想知道是否可以创建一个数组,其中每个值都链接到要执行的相应方法。

例如:如果我有一个从 0 到 99 的数字,并且我想根据我选择的任何数字运行某个方法。我可以制作一个具有相应方法的数组,而不是制作一个包含 100 种不同情况的 if 脚本吗?所有的方法都在一个类中,每个数字都有自己的方法。它看起来像这样:

int num = 73;
methods[num](parameters);

我不知道这样的事情是否可行。非常感谢任何帮助!

【问题讨论】:

  • 你对每个数字或数字范围有一个方法吗(比如对从 10 到 20 的所有数字运行方法 1)
  • 定义一个接口(我们称之为ScriptTask),定义要执行的方法(我们称之为execute)。创建 ScriptTask 的数组tasks。然后使用 tasks[num].execute(parameters);
  • 每个号码都有自己的方法。
  • 每个数字都有自己的方法,但是所有的方法是否都与一个特定的类相关?
  • 如何声明方法和数组?我不习惯使用接口。我知道它们是什么,但我不确定如何正确声明它们。

标签: java arrays syntax


【解决方案1】:

定义一个接口(我们称之为ScriptTask),定义要执行的方法(我们称之为execute)。创建ScriptTask的数组任务。然后使用tasks[num].execute(parameters)

例子:

@FunctionalInterface
interface ScriptTask {
    void execute(String s, int i);
}

class Script {

    private ScriptTask[] tasks;

    Script() {
        this.tasks = new ScriptTask[2];
        this.tasks[0] = this::foo;
        this.tasks[1] = this::bar;
    }

    private void foo(String s, int i) {
        System.out.println(s);
    }

    private void bar(String s, int i) {
        System.out.println(i);
    }

    void run() {
        for (int i = 0; i < tasks.length; i++) {
            tasks[i].execute("hello", i);
        }
    }

    public static void main(String[] args) {
        new Script().run();
    }
}

请注意,如果您在一个类中确实有 100 个不同的方法,这清楚地表明它应该被重构为几个不同的更简单的类,以便更容易阅读、理解和测试。以上是重构的第一步:你可以将每个方法重构为一个单独的类,实现 ScriptTask 接口。

【讨论】:

  • 好的。感谢您的帮助!
【解决方案2】:

您需要传递定义方法的类的对象,从数组中获取方法名称并传递参数。只发布所需的代码。

public static Object invokeExactMethod(Object object, String methodName, Object... args) {
        if (null == args) {
            args = ArrayUtils.EMPTY_OBJECT_ARRAY;
        }
        int arguments = args.length;
        Class<?>[] parameterTypes = new Class[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeExactMethod(object, methodName, args, parameterTypes);
    }


    invokeExactMethod() is method provided my MethodUtils class of apache commons.

希望这会有所帮助..!!

【讨论】:

  • 这是反射的一个很好的用途,但是如果我们试图实现一个方法数组,你为什么不做一个方法数组呢?
  • 我们可以将所有的方法名称存储在一个数组中,无论需要调用哪个方法,我们只需要从数组中获取该方法名称并将其与类和参数一起传递给该方法即可。
  • 在 String[] 中存储方法的名称是个好主意;但我认为我的 Method[] 方法更接近 OP 想要的。
  • 是的,你说的可能是对的,但给定的要求也可以通过这种方式实现。基于案例 ex: 73 可以作为数组中的索引,从数组中获取对应的方法名。
  • 投票为有帮助,它是一个聪明且解耦的解决方案,但是调用 invokeExactMethod(MyClass,"methodName",args[]) 并不是 OP 所期望的,无论如何都非常有用。
【解决方案3】:

可以创建一个接口来保存要运行的方法,例如:

public interface MethodRunner {
   public void run();
} 

定义数组并添加 MethodRunner 的实例。

MethodRunner[] methodsRunners = new MethodRunner[n];
methodsRunners[0] = new MethodRunner() {
      @Override
      public void run() {
        // some method here 
      }
};
methodsRunners[0] = ... 
      . 
      . 
methodsRunners[n] = ... 

并通过以下方式运行它:

methodsRunners[n].run();

【讨论】:

  • 这正是命令模式,我们开发者需要熟悉我们使用的术语
【解决方案4】:

嗯,我认为我们可以利用反射来实现这一点,如下所示"

1.) 存储方法信息的类

public class MethodInfo {

    public String methodName;
    public Class<?>[] params;
}

2.) 由所有方法组成的类

public class Task {

    public void method1(int x, ......){

    }
    And So On..
}

3.) 主类创建数组并使用它。这里使用反射我们可以访问数组中的方法并调用它们。

public class MainClass {

    public static void main(String[] args) {

        MethodInfo firstMethod = new MethodInfo();
        firstMethod.methodName = "method1";
        firstMethod.params = Array OF Parameter Types(.class values)

        MethodInfo[] methods = new MethodInfo[10];
        methods[1] = firstMethod;

        java.lang.reflect.Method method = Task.class.getMethod(methods[1].methodName, methods[1].params);
        method.invoke(new Task(), param1, param2,....);
    }
}

【讨论】:

    【解决方案5】:

    除了 JB Nizet 的答案之外,还可以使用其他语法变体:

        this.tasks = new ScriptTask[4];
        this.tasks[0] = this::foo;
        this.tasks[1] = this::bar;
        this.tasks[2] = (String s, int i) -> {
            System.out.println(s);
        };
        this.tasks[3] = (String s, int i) -> System.out.println(s);
    

    甚至:

       this.tasks = new ScriptTask[] {
            this::foo,
            this::bar,
            (String s, int i) -> {
                System.out.println(s);
            },
            (String s, int i) -> System.out.println(s)
        };
    

    【讨论】:

      【解决方案6】:

      在我正在处理的程序中,我通过创建具有覆盖方法的构造函数来实现这一点。

      public class Script 
      {
          public Script()
          {
      
          }
      
          public void run()
          {
      
          }
      }
      
      public class TestScript extends Script
      {
          public TestScript()
          {
      
          }
      
          public void run()
          {
              //This Method overwrites the run method of script. This is where your code goes.
          }
      }
      
      public class Main
      {
          public static Script test = new TestScript();
      
          public static void main(String[] args)
          {
              test.run();
          }
      }
      

      【讨论】:

      • 是的,他们可以,您可以使用@JB 回答的@FunctionalInterface 来实现它
      • 我不知道这一点,因为我很久以前搜索过这样的解决方案,非常整洁。我也许可以利用这个。
      • @ngueno 从概念上讲,函数式接口只有一个抽象方法。请不要混淆他,因为无论接口声明中是否存在 FunctionalInterface 注释,编译器都会将任何满足功能接口定义的接口视为功能接口。根据文档docs.oracle.com/javase/8/docs/api/java/lang/…
      • 那么答案就是简单地使用接口作为通过来运行/执行某些方法,以便像层次结构一样抽象它
      【解决方案7】:

      我想知道是否有办法将数组值链接到某个方法。

      您可以使用反射来生成方法数组。但是,这是一个“不理想的解决方案”,因为它会使您的代码难以维护。所以,我不得不说这不是一个好习惯,但就是这样。

      package methodarray;
      
      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      
      public class MethodArray {
          public static void main(String[] args) {
              Method[] arrayOfMethods = new Method[100];
              try {
                  //...You will need to add the methods by their name, you can use for loops to add them quickly
                  arrayOfMethods[1] = MethodArray.class.getDeclaredMethod("someMethodName");
                  arrayOfMethods[2] = MethodArray.class.getDeclaredMethod("someOtherMethod");
                  //...
              } catch (NoSuchMethodException | SecurityException ex) {
                  //this can throw an Exception if the method is not found, handle it here
              }
              //now to call a method from the array just do
              try {
                  arrayOfMethods[1].invoke(MethodArray.class);
              } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                  //...handle exceptions
              }
          }
      
          public void someMethodName(){
              //...logic
          }
      
          public void someOtherMethod(){
              //...logic
          }
      }
      

      这将帮助您按照自己的意愿调用方法,但请记住,为了安全起见,java 在使用反射时可能会抛出一些期望。要发送其他参数,只需调用 arrayOfMethods[n].invoke(MethodArray.class, args[]);

      这个问题主要是针对Java的,但是如果它适用于所有面向对象的编程语言,那就很好了。

      为了解决这些类型的常见场景,我们使用设计模式,模式是针对常见场景的经过测试的解决方案。所有说你应该“使用接口”的答案实际上都是在试图帮助你实现这一点,你可以使用的模式称为Command Pattern,你可以阅读它here。了解或阅读该模式可能没有用,因为您说过您不熟悉使用接口。 (我个人一直推荐Head First Design Patterns作为模式介绍书。)

      一旦您了解了命令模式,您就可以简单地生成一组命令,这是一个更好的 OO 解决方案,请记住这一点。我不会做例子,因为我认为如果你对如何使用接口没有清晰的概念很难理解,请阅读它们,这将是值得的!!!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-20
        • 1970-01-01
        • 2021-03-06
        • 1970-01-01
        • 1970-01-01
        • 2012-04-06
        • 1970-01-01
        相关资源
        最近更新 更多