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?

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
Andreas
  • 7,470
  • 10
  • 51
  • 73

5 Answers5

4

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
Community
  • 1
  • 1
Woot4Moo
  • 23,987
  • 16
  • 94
  • 151
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() {
  }

}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • @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. – OldCurmudgeon Sep 10 '13 at 12:19
  • Sure agree. I would treat it as just another "interface" between two modules. Just wanted to mention the scaling piece for future readers. – Woot4Moo Sep 10 '13 at 12:20
  • 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. – OldCurmudgeon Sep 10 '13 at 12:24
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);
}
duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 3
    Thankfully this will be changing in Java 8. – arshajii Sep 10 '13 at 11:52
  • 3
    You can do it with reflection. Kinda messy, though. – Hot Licks Sep 10 '13 at 11:52
  • I think adding something like a C# delegate would be a good thing. Why shudder? – duffymo Sep 10 '13 at 12:57
  • @HotLicks What do you mean? – arshajii Sep 10 '13 at 13:03
  • @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. – Hot Licks Sep 10 '13 at 14:57
  • 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. – duffymo Sep 10 '13 at 15:08
2

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

johan d
  • 2,798
  • 18
  • 26
  • 1
    maybe this would be better served as a comment? – Woot4Moo Sep 10 '13 at 11:56
  • @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. – johan d Sep 11 '13 at 09:26
  • ah yes agree it wasn't in other answers, but it didn't provide a code based solution like the others :) – Woot4Moo Sep 12 '13 at 10:10
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
  }
Gyanendra Dwivedi
  • 5,511
  • 2
  • 27
  • 53