0

I have a TupleList with two entries and I want to add a third (like a reference to a method), my TupleList looks like:

public class NodeList<T, I> {
    public T type;
    public I id;

    public NodeList (T type, I id){
        this.type = type;
        this.id = id;
    }

    public void setT(T type){
        this.type = type;
    }
    public void setI(I id){
        this.id = id;
    }
    public T getT() { 
        return type; 
    }
    public I getI() { 
        return id; 
    }
}

And I have two Strings inside:

public static List<NodeList<String,String>> nodeList = new ArrayList<NodeList<String,String>>();

Every time I have a specific type, I want to call a related method/reference to my method. Any suggestions on how to do that? I don't want to use if or switch as I have some methods.

I create custom Nodes inside a parent AnchorPane on ButtonClick Event. All Nodes have the same Layout at the beginning, all have a Button on them and I add a unique id to every Node on creation (Buttons are created dynamically).

Now I want to link my types to different methods which will be executed when I click on the Button. For this I want to add also a reference to a function which is related to my type.

EDIT 1:

e.g.: My Button is called "RGB to Greyscale", the String type of my Node is rgbgrey. When I click on the Button I create a Node (just a draggable AnchorPane with a Button on it and a Label on top with the String of my type). I have a Button event, where I want to execute different methods, depending on the Buttons I clicked to create my Nodes. Therefore I create a List with different entries. After creating my Node I add a unique String id and set the String type name to rgbgrey. Now I want to call the method rgbtogrey(); when I click on my Node Button.

Another Button is called "Rotate Image", the String type is now rotimg. The layout of my Node is completely the same. When I click now on the Button on my Node, I want to execute a different function in my Button event and call the method rotateimg();.

I create the Nodes like this inside my Node class:

void addRotateNode(ActionEvent event){
    Node nde = new Node();

    id = nde.getId();
    type = new String("rotimg");
    nodeList.add(new NodeList<String, String>(type, id)); //add id and type to list
    nde.nodeLayout(); // This is where I set my Layout
    //...
}

Right now I try this, but I want to avoid switch, because I'll add many more functions.

@Override
public void handle(MouseEvent event) {
    for(int i = 0; i <= ViewController.nodeList.size(); i++){
        String func = ViewController.nodeList.get(i).getT();
            switch(func){
            case "rgbgray":
                //rgbtogrey();
                break;
            case "rotimg":
                //rotateimg();
                break;
        }   
    }
}});

I have a custom EventHandler class:

public class ActionEventHandler implements EventHandler<ActionEvent> {

    private final EventHandler<ActionEvent> eventHandler;
    private final String name;

    public ActionEventHandler(String name, EventHandler<ActionEvent> eventHandler) {
        Objects.requireNonNull(name);
        Objects.requireNonNull(eventHandler);
        this.name = name;
        this.eventHandler = eventHandler;
    }

    @Override
    public String toString() {
        return name;
    }

    @Override
    public void handle(ActionEvent event) {
        eventHandler.handle(event);
    }

}

EDIT 2:

I changed my List to:

public static List<NodeList<String,String, EventHandler<ActionEvent>>> nodeList = new ArrayList<NodeList<String,String, EventHandler<ActionEvent>>>();

and I modified my Node creation to:

void addRotateNode(ActionEvent event){
    Node nde = new Node();

    id = nde.getId();
    type = new String("rotimg");
    EventHandler<ActionEvent> handler = new ActionEventHandler(type, this::rotimg);
    nodeList.add(new NodeList<String, String, EventHandler<ActionEvent>>(type, id, handler));
    nde.nodeLayout(); // This is where I set my Layout
    //...
}
Flippy
  • 202
  • 3
  • 12
  • What specific method do you mean? There are always the same one with your example. – Murat Karagöz Nov 29 '16 at 12:29
  • It is not clear what your try to achieve, please clarify a little bit more. – Nicolas Filotto Nov 29 '16 at 12:30
  • I mean calling methods from a different class, I add objects with type and a id to my parent window. There are different types of Nodes, so when I click on Button test1, I create a custom Node test1 with a Button on it. I want to set the function on this button to the function I connected to test1. The Buttons are created dynamically and all Nodes have the same Layout. – Flippy Nov 29 '16 at 12:33
  • Edited my question, I hope it gets clearer. – Flippy Nov 29 '16 at 12:39
  • @Flippy Please add an use case example. It's still unclear. – Murat Karagöz Nov 29 '16 at 12:44
  • Possible duplicate of [Using Command Design pattern](http://stackoverflow.com/questions/2015549/using-command-design-pattern) – AxelH Nov 29 '16 at 14:13
  • I want to know how I can use a method/reference to method inside a List to link it to a special kind of node I create – Flippy Nov 29 '16 at 14:17

1 Answers1

1

From what I have understood, you should use a pattern, the Command pattern. Since we can't pass a method's reference like C (or other), this pattern is a the one to use to fixed this problem.

This is basicly an Interface

interface Command {
    void execute(); //Could take parameter, return something, your choice.
}

For each "function", we will implements a version of Command. From this instance, we have a sort of reference that can call a method.

Then, you can store any implementation of this interface into a Collection.

Just a simple example :

List<Command> list = new ArrayList<>();
list.add(new ForwardCommand()); //An implementation of the interface
list.add(new RecordCommand());  //An other one
list.add(new ClearCommand());   //An other one

And you just need to get on of those instance an call the interface method to execute the function.

Command c = list.get(0); //Get forwarCommand
c.execute(); //That will execute the functionnality of the instance selected

Note : This could be done using Reflexion but I found that cleaner an more maintainable. (and less risky)

AxelH
  • 14,325
  • 2
  • 25
  • 55
  • How could I store this inside my List with 3 entries? `public static List> nodeList = new ArrayList>();`? – Flippy Nov 29 '16 at 15:13
  • @Flippy From what I can see in `NodeList` T is the name of the function and I the ID. So Use `NodeList` and getT() will return a `Command` instance that you will be able to execute – AxelH Nov 29 '16 at 15:15
  • How do I exactly call the method? I try this: `Command cmd = OperationProperty.execute();` OperationProperty is my class which implements Command and where I have my `public void execute(){} ` (now without any functionality) then I try to add cmd to my list: `nodeList.add(new NodeList(type, id, cmd));` But it says on `OperationProperty.execute();` cannot convert void to command, because my function is void, but I try to convert it to command. – Flippy Nov 29 '16 at 22:43
  • @Flippy You need to pass an instance of `OperationProperty` (`new (OperationProperty()`) to the `Nodelist`. You can't pass directly a method (because you only get the result, no such thing like method's references, pointers... , so you pass an instance that will contain the method. Later, you will recover the instance an call `execute()` to run the functionnality. – AxelH Nov 30 '16 at 06:15
  • So I can't add it in a List with more than 1 entry? I don't get how I can do sth like `Command cmd = OperationProperty.myfunction();` to store it later into my list where I use `cmd` – Flippy Nov 30 '16 at 07:19
  • @Flippy But you don't want to store the result of the function. The command is created to be able to store something into a list (because we can't store a method). So you store `OperationProperty` (if this is an instance, this should be named `operationProperty`) to call `execute();` later based on the instance will get. And I don't understand your first question, you can add every NodeList you want, even if it use the same Command twice. But this is a basic implementation, you have just not understand why we are doing this. – AxelH Nov 30 '16 at 07:31
  • My problem is when I click on the Button to create my entries of my List (type, id) I want also add a instance/reference which has the same index as type/id have. So when I identify my Button with the specific id, I want to add a specific function which should be added to my list when I create the Node. I know I can't store a method. But when I just store `OperationProperty cmd = new OperationProperty();` I don't specify my method – Flippy Nov 30 '16 at 07:38
  • I thought of accessing it through: `for(int i = 0; i < ViewController.nodeList.size(); i++){ String id2 = self.getId(); String id = ViewController.nodeList.get(i).getI(); if(id == id2){ Command c = ViewController.nodeList.get(i).getL(); c.execute(); } }` – Flippy Nov 30 '16 at 07:45
  • But `OperationProperty` IS the method wrapped in an instance, the methods will be in `execute()`. For each function, you will have one Command. If you want an example, please provide a [mcve] that I could use – AxelH Nov 30 '16 at 07:48
  • I just don't know how to write this from you example: `List list = new ArrayList<>(); list.add(new ForwardCommand()); //An implementation of the interface list.add(new RecordCommand()); //An other one list.add(new ClearCommand()); ` so that I can store it to a list with a single entry than to a List witch multiple entries. – Flippy Nov 30 '16 at 07:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/129406/discussion-between-axelh-and-flippy). – AxelH Nov 30 '16 at 07:48