-1

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. Since by using "if", I need to list all the possible values of "model" and judge which method I should use.

Thank you very much for help. I am new to 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;

 } 
mtmk
  • 6,176
  • 27
  • 32
Crystal
  • 37
  • 4

3 Answers3

2

One way you can approach this is to use Reflection:

Method method = myClass.getClass().getMethod("doSomething", null);
method.invoke(myClass, null);
JustSoAmazing
  • 747
  • 4
  • 8
  • Instead of the `null`s, there should just be one parameter in each function. The `null`s cause `non-varargs call of varargs method with inexact argument type for last parameter` warnings. And as a note to @user3831001, this code must be surrounded by a try-catch. It throws a few checked exceptions--as it should. – aliteralmind Jul 11 '14 at 22:18
  • Right. This needs to be a little more complicated if it is intended to support "no arguments" calls to varargs methods. You'd need to check both cases when finding `method`, and then if a varargs method is found, pass in an empty `Object[]`. – Dawood ibn Kareem Jul 11 '14 at 23:31
0

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. 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

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();
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
0

Since you are new to Java, it's time for some general pointers:

  1. In Java, we usually name our methods with camelCase, so the first letter is lower case.
  2. Also, in Java we usually leave the opening curly-bracket on the same line as the code (no newline).
  3. Always use final on your variables. 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.
  6. Use the equals-method to compare Strings.
  7. If you only want one result, use 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 Strings around, and use them as a basis for creating objects. 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.

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:

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 Strings 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.

One could also leave the linking between operation and String to the enum, like this:

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:

public static SerializedExpFamMixture rateMtxModel(String model) {
    return Something.fromString(model).getCorrespondingSerializedExpFamMixture();
}
Tobb
  • 11,850
  • 6
  • 52
  • 77