Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: store method calls in an array and execute later?

I've looked around but can't find exactly what I'm looking for.

What I have going on is I have a class defined that is meant to represent a region in a JPanel that you can draw to which it does by creating a bufferedImage of the specified size and uses that images graphics to doubleBuffer to the JPanel, and that region is then drawn as an image to the parent JPanel, essentially creating panel regions without having to deal with javax's crazy panel organizing logic that depends on all panels touching boarders. This is essentially a graphics context for graphics widgets that can move/resize etc, similar to video game UI.

What I'm trying to do is I want to be able to store method calls to draw operations in the graphics class, parameters included. The purpose for this is so that I can, either at runtime or in source code, load in methods with the parameter's values already specified that can just be called without having to break encapsulation, because as it stands either the parent class has to draw directly to the bufferedimage, something I would like to avoid so that methods can be added at runtime, or the method calls for drawing have to be done in the PanelRegion class itself, forcing me to create a new specialized PanelRegion every time I want another PanelRegion, which just won't work effeciently.

What I would like to be able to do is simply something like:

Class graphics = panelRegion.getGraphics();
String methodName = "drawRectangle";
int xPos = 0;
int yPos = 0;
int width = 0;
int height = 0;

ImaginaryMethodClass method = graphics.getMethod(methodName, int, int, int, int);
method.imaginaryMethodThatLoadsParameterValues(xPos, yPos, width, height);

panelRegion.addMethod(method);
panelRegion.invokeDrawMethods();

public void invokeDrawMethods()
{
for(ImaginaryMethodClass aMethod : listOfMethods)
{
aMethod.imaginaryMethodThatExecutesTheMethodWithTheLoadedParameterValues();
}
}

If this isn't making sense, essentially the only solution I've found is you can abstractly load methods into arrays with the reflector class, however when you want to execute you have to still send the values for the parameters of that method, essentially making it a complicated method call in source code. I want to either cut that step out or make it so the method can execute itself with the values I've given.

like image 342
Phrygian Phan Avatar asked Jun 15 '14 22:06

Phrygian Phan


Video Answer


1 Answers

What you are trying to do is called the Command Pattern.

A simple way to do this in Java is to create an anonymous class which implements Runnable. Runnable is an interface which defines a single method: void run(). You can implement an anonymous class anywhere in your code, and when you do, you can reference any variable in the current scope.

Java 8 makes this a lot easier by adding some syntactic sugar for functional interfaces and lambda expression. The code in your question would look like this in Java 8:

Graphics graphics = panelRegion.getGraphics();

Runnable methodCall = () ->  graphics.drawRectangle(xPos, yPos, width, height);

panelRegion.addMethod(methodCall);

When you don't use Java 8 yet, the code is a bit more convoluted:

Graphics graphics = panelRegion.getGraphics();

Runnable methodCall = new Runnable() {
   public void run() {
       graphics.drawRectangle(xPos, yPos, width, height);
    }
}

panelRegion.addMethod(methodCall);

In both of the (completely equivalent) examples above, graphics.drawRectangle(xPos, yPos, width, height) is not executed. It is only executed when methodCall.run() is called.

The implementation of panelRegion would maintain a List<Runnable> with the commands it is going to execute. When it executes that list, it just calls the .run() method on each of them.

class PanelRegion {

    private List<Runnable> listOfMethods = new ArrayList<>();

    public void addMethod(Runnable methodCall) {
        listOfMethods.add(methodCall);
    }

    public void invokeDrawMethods()
    {
         for(Runnable aMethod : listOfMethods) {
             aMethod.run();
         }
    }
}
like image 165
Philipp Avatar answered Nov 15 '22 19:11

Philipp