tags:

views:

670

answers:

7

I am looking for a way to pass a parameter by reference. I understand that Java does not pass methods as parameters, however, I would like to get an alternative.

I've been told interfaces are the alternative to passing methods as parameters but I don't understand how an interface can act as a method by reference. If I understand correctly an interface is simply an abstract set of methods that are not defined. I don't want to send an interface that needs to be defined every time because several different methods could call the same method with the same parameters.

What I would like to accomplish is something similar to this:

public void setAllComponents(Component[] myComponentArray, Method myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod(leaf);
    } //end looping through components
}

invoked such as:

setAllComponents(this.getComponents(), changeColor());
setAllComponents(this.getComponents(), changeSize());

Thanks in advance, Mac

A: 

Use the java.lang.reflect.Method object and call invoke

Vinodh Ramasubramanian
Ouch. Ouch squared.
EricSchaefer
I don't see why not. The question is to pass a method as a parameter and this a very valid way of doing it. This can also be wrapped in to any number of pretty looking pattern to make it look good. And this is as generic as it gets without the need for any special interfaces.
Vinodh Ramasubramanian
+9  A: 

Take a look at the command pattern.

// NOTE: code not tested, but I believe this is valid java...
public class CommandExample 
{
    public interface Command 
    {
        public void execute(Object data);
    }

    public class PrintCommand implements Command 
    {
        public void execute(Object data) 
        {
            System.out.println(data.toString());
        }    
    }

    public static void callCommand(Command command, Object data) 
    {
        command.execute(data);
    }

    public static void main(String... args) 
    {
        callCommand(new PrintCommand(), "hello world");
    }
}

Edit: as Pete Kirkham points out, there's another way of doing this using a Visitor. The visitor approach is a little more involved - your nodes all need to be visitor-aware with an acceptVisitor() method - but if you need to traverse a more complex object graph then it's worth examining.

Dan Vinton
this is exactly what I am looking for!
@Mac - good! this one comes up again and again in languages without first-class methods as the de-facto way of simulating them, so it's worth remembering.
Dan Vinton
It's the visitor pattern (separate the action of iterating over a collection from the function applied to each member of the collection), not the command pattern (encapsulate the arguments for a method call into an object). You are specifically not encapsulating the argument - it is provided by the iteration part of the visitor pattern.
Pete Kirkham
No, you only need the accept method if you're combining visiting with double dispatch. If you have a monomorphic visitor, it's exactly the code you have above.
Pete Kirkham
A: 

Use the Observer pattern (sometimes also called Listener pattern):

interface ComponentDelegate {
    void doSomething(Component component);
}

public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) {
    // ...
    delegate.doSomething(leaf);
}

setAllComponents(this.getComponents(), new ComponentDelegate() {
                                            void doSomething(Component component) {
                                                changeColor(component); // or do directly what you want
                                            }
                                       });

new ComponentDelegate()... declares an anonymous type implementing the interface.

EricSchaefer
This is not the pattern you are looking for.
Pete Kirkham
What are you talking about?
EricSchaefer
The observer pattern is about abstracting the ability to respond to a change. The OP wants to abstract the action taken at each item in a collection away from the code iterating over the collection, which is the visitor pattern.
Pete Kirkham
The Observer/Listener pattern is actually the same as that Command pattern. They only differ in intention. The observer is about notification while the command is a substitute for first class functions/lambdas. The visitor on the other hand is something completly different. I don't think it can be explained in a couple of sentences so please have a look at http://en.wikipedia.org/wiki/Visitor_pattern
EricSchaefer
+1  A: 

First define an Interface with the method you want to pass as a parameter

public interface Callable {
  public void call(int param);
}

Implement a class with the method

class Test implements Callable {
  public void call(int param) {
    System.out.println( param );
  }
}

// Invoke like that

Callable cmd = new Test();

This allows you to pass cmd as parameter and invoke the method call defined in the interface

public invoke( Callable callable ) { callable.call( 5 ); }

stacker
+1  A: 

Last time I checked, Java is not capable of natively doing what you want; you have to use 'work-arounds' to get around such limitations. As far as I see it, interfaces ARE an alternative, but not a good alternative. Perhaps whoever told you that was meaning something like this:

public interface ComponentMethod {
  public abstract void PerfromMethod(Container c);
}

public class ChangeColor implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public class ChangeSize implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod.PerfromMethod(leaf);
    } //end looping through components
}

Which you'd then invoke with:

setAllComponents(this.getComponents(), new ChangeColor());
setAllComponents(this.getComponents(), new ChangeSize());
you spelled it out +1
A: 

Java do have a mechanism to pass name and call it. It is part of the reflection mechanism. Your function should take additional parameter of class Method.

public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled)
{
...
Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist);
...
}
David Gruzman
A: 

You could see some functional libraries for Java, like http://code.google.com/p/functionaljava/

Arturo Tena