How to implement a Java method, that will call another method, based on the name of the calling class?

问题: We have a Java class, WebCenterGrid. This class is full of methods to do things in a grid such as finding a row, finding a cell value, sorting a column. We have several cla...

问题:

We have a Java class, WebCenterGrid. This class is full of methods to do things in a grid such as finding a row, finding a cell value, sorting a column. We have several classes that use this class. The classes using it all refer to different grids, but the functionality is the same.

The only thing that differs is how to create the grid. Some classes do a search which populates the grid (search also refreshes). Some do an updateList() to update the grid, etc.

I would like to add a method to WebCenterGrid to refresh the grid. The problem is as I said each method has a different name.

I somehow want to pass into WebCenterGrid the name of a method to call to do the refresh. I have done some searches and found something about lambda which I did not really understand.

I haven't used C++ in a while but there was some way to pass a method into those methods. This class is in Java not C++, but is there some sort of understandable equivalent?

 public class WebCenterGrid {
    ....
    ....
    public void refresh(Method meth) {
           meth();
    }
 }

回答1:

Basically, there are two ways.

One is to use reflection, this means: relying on runtime type information, commonly derived from raw strings. Like saying: I have some object of class X, and I want to invoke the method named "doTheFoo()" on that object.

See here for all the glory details.

A slightly better way is to use the MethodHandle class, instead of the "raw" reflection Method class. See here for handles.

But then: reflection is happening at runtime. Your code compiles fine, but if you get any detail wrong, it blows up at runtime.

Thus I suggest looking into lambdas, based on Function, see here.


回答2:

Instead of having a Method parameter, accept an Interface, and the implementation will define what will be called.

You can use lambdas here as well if you'll define your interface as Functional Interface.

Example:

public class Main {

    public static void main(String[] args) {
        act(new Run());
        act(new Swim());
        // Passing a body of the function you want to execute
        act(() -> System.out.println("walking"));
    }

    public static void act(Action action) {
        action.act();
    }
}

@FunctionalInterface
interface Action {
    void act();
}

class Run implements Action {
    @Override
    public void act() {
        System.out.println("running");
    }
}

class Swim implements Action {
    @Override
    public void act() {
        System.out.println("swimming");
    }
}

Output:

running
swimming
walking

If you have predefined refresh logic, you can create association resolver based on mapping which will help you to define proper service based on some conditions.

public class Main {

    static Map<ActionType, Action> actionResolver = new HashMap<>();

    // Static init is just for brevity sake
    static {
        actionResolver.put(ActionType.RUN, new Run());
        actionResolver.put(ActionType.WALK, new Walk());
        actionResolver.put(ActionType.SWIM, new Swim());
    }

    public static void main(String[] args) {
        act(ActionType.RUN);
        act(ActionType.WALK);
        act(ActionType.SWIM);
    }

    public static void act(ActionType actionType) {
        Action action = actionResolver.get(actionType);
        if (action == null)
            throw new IllegalArgumentException("ActionType was not registered");

        action.act();
    }
}

enum ActionType {
    RUN,
    SWIM,
    WALK
}

Output is the same as above.


回答3:

Well, since we can't see any of your code, I'll suggest the following solution, that's based on my personal assumption about how your code works.

Please keep in mind that this method is not so scale-able and pretty inefficient if you have 100 different ways of creating grids.

However, if you have (e.g. 3) types of such ways for example, you can use constants!

See below:

 public class WebCenterGrid {
    //Declare constants with meaningful names for grid creation (add more as you like)
    public static final int DEEP_COPY=1, SEARCH=2, REBUILD=3;

    public void makeDeepCopy(){
    //implementation goes here..
    }

    public void searchAndPopulate(){
    //implementation goes here..
    }

    public void rebuildGrid(){
    //implementation goes here..
    }

    public void refresh(int operation) {
        switch(operation) {
        //based on 'operation', call appropriate method!
        case DEEP_COPY: this.makeDeepCopy(); break;
        case SEARCH: this.searchAndPopulate(); break;
        case REBUILD: this.rebuildGrid(); break;
        //you can have a default operation for any parameter that is not
        //in the list of our defined constants(i.e. the number 143)
        default: simpleRefresh(); break;
        }
    }
 }

So what makes the above solution work?

Basically, when you call refresh(int operation) from one of your other classes, you need to pass an int as a parameter. That integer is one of the constants defined at the very top of the class. According to which constant was passed, the switch case will determine which method to call.

EXAMPLE (Let's say that AwesomeGridCreator is a class that when it calls refresh(), in order to update a grid, it has to do a search and then populate the grid (like you mention in your question).

We name an integer (for simplicity) SEARCH_POPULATE and we give it ANY value we want. For example 286.

We can then use that constant from any other class, because we don't care what its value is (in this case 286, but the functionality it provides when calling refresh().

public class WebCenterGrid {
    /*some code here*/
    public static final int SEARCH_POPULATE = 286; //integer value doesn't matter

    public void refresh(int operation) {
        switch(operation) {
        case SEARCH_POPULATE: this.searchAndPopulate(); break;
    }


    /*...some other code here, we don't care..*/
}

Then, at the 'calling' class:

public class AwesomeGridCreator{
  //some code here

  WebCenterGrid wcg = new WebCenterGrid();
  //The parameter that we pass below (2), will make the refresh() method call
  //the method that we defined in our switch cases ('searchAndPopulate()').
  wcg.refresh(wcg.SEARCH_POPULATE);
}
  • 发表于 2019-01-19 04:15
  • 阅读 ( 225 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除