【问题标题】:Java - How to invoke a method from another class, with the class name and method name as a stringJava - 如何从另一个类调用方法,类名和方法名作为字符串
【发布时间】:2016-06-16 05:36:23
【问题描述】:

这个方法被定期调用

public static void stynax(String N[]) {
    if (N[1].equals("echo")) { echo.s(); main(); }
    if (N[1].equals("detectos")) { detectos.s(); main(); }
    if (N[1].equals("getuser")) { getuser.s(); main(); }
    if (N[1].equals("exit")) { exit.s(); main(); }
    if (N[1].equals("makefile")) { makefile.s(); main(); }
    if (N[1].equals("cd")) { cd.s(); main(); }
    if (N[1].equals("system")) { system.s(); main(); }
    main();
}

如何调用所有这些方法

system.s();
echo.s();

等,通过查看类是否存在,然后调用相应的方法。 N[1] 始终是类名。存储此方法的类位于名为 main 的类中,而被调用的类位于名为 Commands 的不同包中。

我似乎总是遇到这个错误,当我尝试创建一个 Class 变量时,我认为这是主要问题。

at java.net.URLClassLoader.findClass(URLClassLoader.java:381)

所以它永远不会调用该方法。

为了简化。

1) 程序将类名作为字符串获取为 N[1]

2) 查看类是否存在

3) 如果类存在,则通过类的名称调用它 N[1].s();

编辑:使用的进口 导入 java.io.ByteArrayOutputStream; 导入 java.io.FileWriter; 导入 java.io.IOException; 导入 java.io.PrintStream; 导入 java.io.PrintWriter; 导入 java.lang.reflect.InvocationTargetException; 导入java.lang.reflect.Method; 导入 java.util.Arrays;

import cgameing.Commands.FileBrowser;
import cgameing.Commands.banner;
import cgameing.Commands.cd;
import cgameing.Commands.detectos;
import cgameing.Commands.echo;
import cgameing.Commands.exit;
import cgameing.Commands.getuser;
import cgameing.Commands.makefile;
import cgameing.Commands.system;

编辑结束:

这对任何想做同样事情的人都有效

(Class.forName("commands."+N[1])).getDeclaredMethod("s", null).invoke(null,null);

谢谢大家

【问题讨论】:

  • 您的帖子不清楚,请修改您的帖子,使其更清晰。
  • 只是为了澄清......你真的需要动态查找类吗?如果没有,将命令名称映射到 lambda 函数会容易得多。
  • lambda 函数可以工作
  • 你能展示你的进口吗?

标签: java arrays invoke


【解决方案1】:

您需要使用反射。尝试如下。如果您的类在不同的包中,请使用完全限定的类名而不是 "XYZ"

import java.lang.reflect.*;
import java.lang.*;

public class ReflectionTest {
 public static void main(String[] args)throws NoSuchMethodException,
 ClassNotFoundException,
 IllegalAccessException,
 InvocationTargetException {
  (Class.forName("XYZ")).getDeclaredMethod("ABC", null).invoke(null,null);
 }
}

class XYZ
{
  public static void ABC()
  {
    System.out.println("Lulz");
  }
}  

对于您的用例,您的类位于 commands 包中(正如您在评论中所述)。完全限定名称将是 commands.classname

(Class.forName("commands."+N[1])).getDeclaredMethod("s", null).invoke(null,null);

【讨论】:

  • 如何添加字符串数组:
【解决方案2】:

您可以使用反射。 你有类名进入数组。

您可以使用“类”和“方法”类。类可以判断类是否存在,方法可以用来调用你需要调用的方法。

    try {
            Class<?> c = Class.forName(N[1]);
            Object t = c.newInstance();

            Method[] allMethods = c.getDeclaredMethods();
         for (Method m : allMethods) {
              String mname = m.getName();
// if name matches use invoke method.
            }
        } catch (ClassNotFoundException x) {
         //handle exception
        }

如果您需要查看更多详细信息,请咨询 API。

【讨论】:

  • 这一行,阻止我做任何事情 Class> c = Class.forName(N[1]);因为它总是返回 java.net.URLClassLoader.findClass(URLClassLoader.java:381)
  • 您是否使用完全限定的类名(例如 a.b.c.classname)?
  • 这些类在一个名为 commands 的包中?我需要以某种方式引用它们吗
  • 没有得到你的问题。您要问的是哪些课程?
  • 被调用的类 echo.s(); cd.s();等,对不起,我解释得不好。
【解决方案3】:

我建议您尽可能避免使用反射。更好的是定义您希望看到的命令 - 最好在 enum 中。

例如:

enum Command {
    CD(FileSystem::cd),
    EXIT(Application::exit),
    MAKEFILE(FileSystem::createFile),
    ...

    private final Runnable runnable;

    Command(Runnable runnable) {
        this.runnable = runnable;
    }

    public void run() {
        runnable.run();
    }
}

如果您愿意,您仍然可以使用名称来获取命令(如果在枚举中找不到该值,则自动抛出异常 - 这大概是您想要的):

Command.valueOf(commandString.toUpperCase()).run();

或者直接调用命令而不必知道它们委托给哪个方法:

Command.MAKEFILE.run();

假设您将在某处拥有if 语句的列表,您不妨将其封装在enum 中,这比嵌入方法名称要明确得多。

【讨论】:

    【解决方案4】:

    好吧,看来大家都在建议反射,至少还有一种替代方法可以做到,这取决于你在编译时是否知道你的类名和方法名。

    所以,假设我们在某个类中有这个方法:

        public void changeDirectory(String args) {
    
        //do something
    }
    

    如果它们是已知的,您可以轻松地使用方法引用:

        HashMap<String, Consumer<String[]>> commands = new HashMap<>();
        commands.put("cd", SomeClass::changeDirectory);
        commands.get("cd").accept(args);
    

    缺点是,方法签名必须相同... 另一方面,如果您知道确切的方法,您可以使用 switch 语句并直接调用它们...

    如果您想动态地执行此操作,反射的替代方法是 MethodHandles。优点是它们只会在创建时检查一次访问权限,并且有一些其他优化应该在大多数情况下使它们比反射更快。

        Class<?> dynamicallyFoundClass = Class.forName("some.package.SomeClass");
        Class<?> fixedClass = SomeClass.class;
        String methodName = "changeDirectory";
        MethodType type = MethodType.methodType(void.class, String.class);
    
        handles.put("cd", lookUp.findStatic(dynamicallyFoundClass, methodName, type));
    
        handles.get("cd").invoke(args);
    

    但是您必须使用的方法有多复杂取决于您在编译时知道什么以及在运行时必须找到什么。

    【讨论】:

    • 这也可以,因为所有类都使用相同的 3 种方法
    • 我没有建议反思 :-)
    • 我注意到了,但是在我写这篇文章的时候你的答案并不存在。 ;)
    猜你喜欢
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    相关资源
    最近更新 更多