0

Every instance of class A has an instance of class B. A should call different methods in B depending on its member variable method_num. This is an implementation that does what I want:

public class A {
    private B myB = new B();
    public int method_num = 1;
    public callBMethod() {
        if ( method_num == 1 )
            myB.method1();
        else
            myB.method2();
    }
}

public class B {
    public method1() { }
    public method2() { }
}

But instead of doing myA.method_num = 1, I want to be able to somehow pass B's method1 or method2 directly. How can I do that?

7
  • 4
    Read about java reflection. Commented Sep 10, 2013 at 11:51
  • I suppose passing the method name is not the problem, so I changed the question title. Commented Sep 10, 2013 at 11:51
  • Reflection, though it's not particularly straight-forward. Commented Sep 10, 2013 at 11:52
  • @larsmans Unless there's some way of passing a "method object". Commented Sep 10, 2013 at 11:52
  • @Andreas: there's no such a thing, unfortunately. But in any case, you wouldn't be passing the name, then. Commented Sep 10, 2013 at 11:52

5 Answers 5

5

I think you can use reflection like this:

java.lang.reflect.Method method;
try {
  method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) {
  // ...
} catch (NoSuchMethodException e) {
  // ...
}  

try {
  method.invoke(obj, arg1, arg2,...);
} catch (IllegalArgumentException e) {  //do proper handling
} catch (IllegalAccessException e) {//do proper handling
} catch (InvocationTargetException e) {//do proper handling
Sign up to request clarification or add additional context in comments.

3 Comments

I wouldn't recommend empty catch blocks to anyone.
@duffymo I wouldn't either, it was a direct port from the linked answer :)
I wouldn't perpetuate bad answers.
4

If you don't want to use reflection (and this is an excellent goal) then there are some neat features of enums that allow you to set up an enum as a proxy.

public class A {
  private B myB = new B();
  public int method_num = 1;

  public void callBMethod() {
    // Could do it by name.
    BMethods.valueOf("method1").call(myB);
    // Or by number.
    BMethods.values()[method_num].call(myB);
  }

}

enum BMethods{
  method1 {
    @Override
    public void call(B b) {
      b.method1();
    }
  },
  method2 {
    @Override
    public void call(B b) {
      b.method2();
    }
  };

  public abstract void call (B b);
}

public class B {
  public void method1() {
  }

  public void method2() {
  }

}

3 Comments

@Woot4Moo - er ... for each method in class B you need an enum that proxies it. I accept that this becomes cumbersome as the number of methods in B increases but it avoids reflection which, to me, is a laudable gain.
Sure agree. I would treat it as just another "interface" between two modules. Just wanted to mention the scaling piece for future readers.
In the same vein - using this method it is still possible to trace all users of every method of B and be absolutely sure you have them all. Once you use reflection - anywhere in your code - this is no longer possible.
3

You cannot. Java doesn't treat functions as first class objects, because it doesn't have functional features like Python or C#.

You can create a Command interface and pass that object reference:

public interface Command {
    void execute(Object [] args);
}

5 Comments

You can do it with reflection. Kinda messy, though.
I think adding something like a C# delegate would be a good thing. Why shudder?
@HotLicks What do you mean?
@duffymo & arshajii -- Every "improvement" to Java since about 1.2 has been done in the worst possible way, further corrupting what was originally a very "clean" design. It's not the added function, it's the way it's apt to be designed and implemented.
I don't agree about all changes having been done in the worst possible way, but I'll agree with not having much confidence in Oracle. They aren't Sun; they don't have Gosling and Joy to sweat the details.
2

Maybe with Runnable objects ? You can pass from B a runnable, and call .run() directly from A

3 Comments

maybe this would be better served as a comment?
@Woot4Moo: While I agree this is a short (too short?) answer, I thought that using a runnable object is both appropriate and not already in existing answers.
ah yes agree it wasn't in other answers, but it didn't provide a code based solution like the others :)
0

Java reflection API provides you a way, where a Method type of object could be passed along with the target object and then the method could be invoked on the target object.

A sample example is here:

Method m; // The method to be invoked

  Object target; // The object to invoke it on

  Object[] args; // The arguments to pass to the method

  // An empty array; used for methods with no arguments at all.
  static final Object[] nullargs = new Object[] {};

  /** This constructor creates a Command object for a no-arg method */
  public Command(Object target, Method m) {
    this(target, m, nullargs);
  }

  /**
   * This constructor creates a Command object for a method that takes the
   * specified array of arguments. Note that the parse() method provides
   * another way to create a Command object
   */
  public Command(Object target, Method m, Object[] args) {
    this.target = target;
    this.m = m;
    this.args = args;
  }

  /**
   * Invoke the Command by calling the method on its target, and passing the
   * arguments. See also actionPerformed() which does not throw the checked
   * exceptions that this method does.
   */
  public void invoke() throws IllegalAccessException,
      InvocationTargetException {
    m.invoke(target, args); // Use reflection to invoke the method
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.