30

Say I have Method1(void), Method2(void)...

Is there a way i can chose one of those with a variable?

 String MyVar=2;
 MethodMyVar();
Adam Outler
  • 1,651
  • 4
  • 19
  • 23
  • 3
    You can, but why do you want to do this? – jjnguy Nov 09 '10 at 20:57
  • 3
    I actually ran into a case for this once, where using reflection + a HashMap made more sense than a massive if/else of string comparisons and conditional method calls. – cHao Nov 09 '10 at 21:04
  • 1
    @cHao: surely a better scheme would've been to write an interface and use a HashMap to store objects of that interface as an analog for lambda functions. – Mark Elliot Nov 09 '10 at 21:51
  • 2
    @Mark E: Not if all the methods i wanted to call were on the same object. Using an interface would mean having a separate class to represent each method, meaning a whole bunch of classes and a whole bunch of setup work to do what i did more reliably with annotations, reflection, and a HashMap in a whole lot less code. Unless i'm understanding you wrong. – cHao Nov 09 '10 at 22:19
  • 27
    "Why would you want to do this" answers are unproductive, rude, and apple-like. – SSH This Jan 08 '13 at 00:14
  • 1
    @SSHThis No they are not. When a question asks how to do something odd, the asker probably has an XY Problem. – Raedwald Apr 15 '15 at 21:24
  • @Raedwald I have read this argument, but if one is unaware that he or she has an XY problem, wouldn't that be a problem in itself worth identifying and sharing for others to benefit from? I think SSH is encouraging a helpful atmosphere (with the exception of calling out apple?). I, for one, would like to read reasoning from experts such as yourself. – Jonathan Komar Apr 04 '19 at 05:49

4 Answers4

45

Use reflection:

Method method = WhateverYourClassIs.class.getDeclaredMethod("Method" + MyVar);
method.invoke();
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • 1
    +1, with the caveat that reflection can be slow, and type safety is no longer enforced by the compiler. – cHao Nov 09 '10 at 21:02
  • WRT his example, wouldn't this look for 'Method2' as the method? – javamonkey79 Nov 09 '10 at 21:03
  • What dependencies do `Method` and `Class` have? I am trying to use this and I get the error : `non-static method getDeclaredMethod cannot be referenced from a static context` – M Y Apr 29 '15 at 04:35
  • @hellyale: this should be an instance method, you have to get the class object on your class and call the method on that. – Nathan Hughes Apr 29 '15 at 10:49
  • @cHao how much slow, can you tell? – T.Todua Jan 12 '17 at 21:23
  • 1
    @T.Todua: Possibly dozens, maybe even hundreds of times as long. If you're doing it as a one-off, you won't notice the difference, and the flexibility is well worth the possible extra microsecond. In a tight loop, though, it can matter. – cHao Jan 13 '17 at 19:47
  • @cHao and why it doesnt exist a quick way in Java to do so? in `php` and etc, it's much easy. – T.Todua Jan 13 '17 at 20:00
  • @T.Todua: Because Java isn't PHP. :P Strict static typing (like Java's) comes with tradeoffs. It can make the common cases (where you know exactly what you want to call) screaming fast, but at the cost of flexibility. PHP's function calls are slower all around, but dynamism impacts them less. – cHao Jan 13 '17 at 22:20
11

Only through reflection. See the java.lang.reflect package.

You could try something like:

Method m = obj.getClass().getMethod("methodName" + MyVar);
m.invoke(obj);

Your code may be different if the method has parameters and there's all sorts of exception handling missing.

But ask your self if this is really necessary? Can something be changed about your design to avoid this. Reflection code is difficult to understand and is slower than just calling obj.someMethod().

Good luck. Happy Coding.

Todd
  • 3,438
  • 1
  • 27
  • 36
  • `obj` is just a variable name I made up, but it's whatever object you want to reflectively invoke a method on. – Todd Oct 14 '14 at 19:15
  • How to pass a parameters to a reflective method ? – Salman Oct 08 '17 at 05:56
  • See the Javadocs for Method https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object,%20java.lang.Object...) – Todd Oct 08 '17 at 22:19
8

You could use the Strategy design pattern and a mapping from the string you have to the corresponding concrete strategy object. This is the safe and efficient means.

So, have a HashMap<String,SomeInterfaceYouWantToInvokeSuchAsRunnableWithPseudoClosures> look-up.

E.g., something along the lines of:

final static YourType reciever = this;
HashMap<String,Runnable> m = new HashMap<String,Runnable> {{
    put("a", new Runnable() {
       @Override public void run () {
         reciever.a();
       }
    });
    ....
}};
// but check for range validity, etc.
m.get("a").run()

You could also use reflection or "invert" the problem and use polymorphism

efthimio
  • 592
  • 5
  • 19
  • 2
    I corrected `override` to Java's `@Override`, but it's really unnecessary. Also enums might be useful in some related cases. Reflection is, of course, evil. – Tom Hawtin - tackline Nov 10 '10 at 10:05
  • I did only a small amount of research before asking this, but why is reflection evil? It seems very powerful to me, and it looks like it could make writing some code easier. Honestly, what you wrote above is the coolest thing I've seen all week. I have a question, though: is your method of using a hashmap better than the reflection examples on this same thread? – michaelsnowden Nov 12 '13 at 23:14
  • `add("a", ...` should be `put("a", ...` – Mike Purcell Oct 12 '16 at 19:24
  • @michaelsnowden Reflection is considered evil because it can break encapsulation, and eliminates the benefits of compiler-time checks. There are other strategies, like the one named above, that achieve the same result with some more configuration. Though I do admit I understand the appeal of "You can use any public function name defined on the class" (class as configuration), it weakens one of Java's strengths. – Fleep Aug 17 '17 at 21:49
  • 2
    @Fleep yeah after coding 4 years since I asked that, I've definitely come to realize why it's considered evil haha – michaelsnowden Aug 17 '17 at 21:51
1

I'm not sure how the accepted answer works for method.invoke() without first argument of static method being null(put dummy value still works though). According to The Java™ Tutorials:

The first argument is the object instance on which this particular method is to be invoked. (If the method is static, the first argument should be null.)

The following shows a complete examples (Main.java), for both static(by class) VS non-static(by instance), plus additional example for method with argument, import necessary class, catch exception, and also superclass method example.

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

class Love {
   protected void Method4() {
        System.out.println("calls super protected method by instance");
    }

   public void Method5() {
        System.out.println("calls super public method by instance");
    }
}

class Main extends Love {

    static void Method2(int y) {
        System.out.println("by class: " + y);
    }

    void Method3(String y) {
        System.out.println(y);
    }

    public static void main(String[] args) {

        String MyVar = "2";
        String MyAnotherVar = "3";
        String MySuperVar = "4";
        String MySuperPublicMethodVar = "5";
        Main m = new Main();

       try {
            Method method = Main.class.getDeclaredMethod("Method" + MyVar, int.class); //by class
            Method anotherMethod = m.getClass().getDeclaredMethod("Method" + MyAnotherVar, String.class); //by instance
            Method superMethod = m.getClass().getSuperclass().getDeclaredMethod("Method" + MySuperVar); //super method by instance, can be protected
            Method superPublicMethod = m.getClass().getMethod("Method" + MySuperPublicMethodVar); //getMethod() require method defined with public, so even though sublcass calls super protected method will not works
            try {
                method.invoke(null, 10000);//by class
                anotherMethod.invoke(m, "by instance"); //by instance
                superMethod.invoke(m); //super method by instance
                superPublicMethod.invoke(m); //super's public method by instance
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }

       } catch (NoSuchMethodException e) {
           throw new RuntimeException(e);
       } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
       }
    }
}

Output:

$ javac Main.java
$ java Main 
by class: 10000
by instance
calls super protected method by instance
calls super public method by instance
$ 
林果皞
  • 7,539
  • 3
  • 55
  • 70