简体   繁体   English

如何在Java中调用名称为字符串变量值的方法?

[英]How to call a method whose name is the value of a string variable in java?

This is the code of the method that I want to simplify. 这是我要简化的方法的代码。 The method name I call of SerializedExpFamMixture class is exactly the value of "model", my question is how to assign the value of "model" directly as the name of the method instead of using "if" to determine which method I should call. 我调用SerializedExpFamMixture类的方法名称恰好是“模型”的值, 我的问题是如何直接将“模型”的值分配为方法的名称,而不是使用“ if”来确定应调用的方法。 Since by using "if", I need to list all the possible values of "model" and judge which method I should use. 由于使用“ if”,因此我需要列出“ model”的所有可能值并判断我应该使用哪种方法。

Thank you very much for help. 非常感谢您的帮助。 I am new to java. 我是Java新手。

public static SerializedExpFamMixture RateMtxModel(String model)
 {
   SerializedExpFamMixture result=new SerializedExpFamMixture();
   if(model=="kimura1980()")
     result=SerializedExpFamMixture.kimura1980();
   if(model=="accordance()")
     result=SerializedExpFamMixture.accordance();
   if(model=="pair()")
     result=SerializedExpFamMixture.pair();
   return result;

 } 

One way you can approach this is to use Reflection: 解决此问题的一种方法是使用反射:

Method method = myClass.getClass().getMethod("doSomething", null);
method.invoke(myClass, null);

Use reflection, but you need to consider a few things: 使用反射,但是您需要考虑以下几点:

  1. Bug alert! 错误提示! Comparing Strings using == doesn't work as expected in java - use .equals() instead. 使用==比较字符串无法按预期在Java中正常工作-请改用.equals() However, the solution below bypasses that problem 但是,下面的解决方案可以绕过该问题
  2. For the general case, which includes methods not visible to the invoker, you need to consider accessibility, both in finding the method and invoking it 对于一般情况(包括调用者不可见的方法) 在查找方法调用方法时都需要考虑可访问性
  3. You don't need the result variable, and even if using your code, don't need to initialize it 您不需要result变量,即使使用您的代码,也不需要对其进行初始化

Try this: 尝试这个:

String methodName = model.replace("(", "").replace(")", "");
try {
    // getMethod() returns only public methods, getDeclaredMethod() returns any visibility
    Method method = SerializedExpFamMixture.class.getDeclaredMethod(methodName);
    // if the method is not guaranteed to be visible (eg public) you need this:
    method.setAccessible(true);
    return (SerializedExpFamMixture) method.invoke(null); // how to invoke on the class object
} catch (Exception forBrevity) {
    return new SerializedExpFamMixture();
}

Since you are new to Java, it's time for some general pointers: 由于您是Java的新手,所以该是一些通用指针了:

  1. In Java, we usually name our methods with camelCase, so the first letter is lower case. 在Java中,我们通常使用camelCase命名方法,因此首字母为小写。
  2. Also, in Java we usually leave the opening curly-bracket on the same line as the code (no newline). 同样,在Java中,我们通常将左花括号与代码放在同一行(没有换行符)。
  3. Always use final on your variables. 始终在变量上使用final At least your parameters. 至少您的参数。 That way you won't overwrite it, and thus won't have to try to figure out which value it actually has at runtime. 这样,您就不会覆盖它,因此不必尝试找出它在运行时实际具有的值。
  4. Use curly-brackets! 使用花括号! Please! 请!
  5. The result variable is not actually needed. result变量实际上是不需要的。
  6. Use the equals -method to compare String s. 使用equals -method比较String
  7. If you only want one result, use else-if 如果只想要一个结果,请使用else-if

Fixing these things, your method looks like this: 解决这些问题后,您的方法如下所示:

public static SerializedExpFamMixture rateMtxModel(String model) {
   if (model.equals("kimura1980()")) {
     return SerializedExpFamMixture.kimura1980();
   } else if (model.equals("accordance()")) {
     return SerializedExpFamMixture.accordance();
   } else if(model.equals("pair()")) {
     return SerializedExpFamMixture.pair();
   } 
   return new SerializedExpFamMixture();
}  

Next, let's look at what you are actually trying to do here. 接下来,让我们看看您实际上在尝试做什么。 You want to pass some String s around, and use them as a basis for creating objects. 您想传递一些String ,并将它们用作创建对象的基础。 And now, with the advice given here, you will do this using reflection. 现在,根据此处给出的建议,您将使用反射进行此操作。 This does not sound like a very good idea to me. 对我来说,这听起来不是一个好主意。 Say you were to go through with this, and this happened: 假设您要进行此操作,并且发生了这种情况:

rateMtxModel("kinura1980");

Small typo, hard to spot, will give unexpected results. 小错字,很难发现,会产生意想不到的结果。 If you were actually calling a method the compiler would let you know that you messed up, now you will get no warning (btw did you see both errors in that method call?). 如果您实际上是在调用方法,则编译器会告诉您您搞砸了,现在您将不会收到任何警告(顺便说一句,您在该方法调用中是否看到两个错误?)。 The same if someone were to delete the accordance() -method, the compiler would not alert them that this will break the program. 如果有人要删除conference accordance()方法,情况也是一样,编译器不会警告他们这将破坏程序。

If it was up to be I would just use the static factory-methods in SerializedExpFamMixture directly, but if you have to do it like this (if the task at hand is using a String input to create an object) I would do something like this: 如果可以,我将直接在SerializedExpFamMixture使用静态工厂方法,但是如果您必须这样做(如果当前的任务是使用String输入创建对象),则可以这样做:

public enum Something {
    KIMURA1980("kimura1980()"),
    ACCORDANCE("accordance()"),
    PAIR("pair()");

    private final String stringValue;

    private Something(final String stringValue) {
        this.stringValue = stringValue;
    }

    public static Something fromString(final String string) {
        for (final Something something : values()) {
            if (something.stringValue.equals(string)) {
                return something;
            }
        }
        return null;
    }
}

public static SerializedExpFamMixture rateMtxModel(final String model) {
   if (model == null) {
       throw new IllegalArgumentException("model is null!");
   }

   final Something something = Something.fromString(model);

   if (something == null) {
      return new SerializedExpFamMixture();
   }

   switch(something) {
       case KIMURA1980:
           return SerializedExpFamMixture.kimura1980();
       case ACCORDANCE:
           return SerializedExpFamMixture.accordance();
       case PAIR:
           return SerializedExpFamMixture.pair();
       default:
           return new SerializedExpFamMixture();    
   }   
}  

This way, the one place where you will use the String s is in the enum, the rest of the code will use the enum constants and thus have the safety of the compiler to rely on. 这样,将在其中使用String的地方就是枚举,其余的代码将使用枚举常量,因此具有编译器依赖的安全性。

One could also leave the linking between operation and String to the enum, like this: 还可以将操作和String之间的链接留给枚举,如下所示:

interface Operation<T> {
    public T run(); 
}

public enum Something {
    KIMURA1980("kimura1980()", new Operation<SerializedExpFamMixture>() {
                                      public SerializedExpFamMixture run() { 
                                          return SerializedExpFamMixture.kimura1980();
                                      } 
                               }) ,
    ACCORDANCE("accordance()", new Operation<SerializedExpFamMixture>() {
                                       public SerializedExpFamMixture run() { 
                                           return SerializedExpFamMixture.accordance();
                                       } 
                               }),
    PAIR("pair()", new Operation<SerializedExpFamMixture>() {
                           public SerializedExpFamMixture run() { 
                               return SerializedExpFamMixture.pair();
                           } 
                   }),
    DEFAULT(null, new Operation<SerializedExpFamMixture>() {
                          public SerializedExpFamMixture run() { 
                              return new SerializedExpFamMixture();
                          } 
                  });

    private final String stringValue;
    private final Operation<SerializedExpFamMixture> operation;

    private Something(final String stringValue, final Operation<SerializedExpFamMixture> operation) {
        this.stringValue = stringValue;
        this.operation = operation;
    }

    public static Something fromString(final String string) {
        if (string != null) {
            for (final Something something : values()) {
                if (string.equals(something.stringValue)) {
                    return something;
                }
            }
        }
        return DEFAULT;
    }

    public SerializedExpFamMixture getCorrespondingSerializedExpFamMixture() {
        return operation.run();
    }
}

With this setup in the enum (I think the Operation -part can be trimmed out with Java8), the method will be as simple as: 通过枚举中的此设置(我认为可以用Java8修剪Operation -part),该方法将非常简单:

public static SerializedExpFamMixture rateMtxModel(String model) {
    return Something.fromString(model).getCorrespondingSerializedExpFamMixture();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM