开发者

How should I implement a map of string to method in Java?

开发者 https://www.devze.com 2022-12-16 02:59 出处:网络
I have a list of XML tags and a method for each of them inside my class, getting that tag as an开发者_如何转开发 argument and doing its work. So all the methods get the same input and I want to loop t

I have a list of XML tags and a method for each of them inside my class, getting that tag as an开发者_如何转开发 argument and doing its work. So all the methods get the same input and I want to loop through the list of tags, calling appropriate method each time.

In Python I've made it with a hash of strings (names of tags) to lambdas (self.methodName()) and I want to translate it to Java.

Now in Java, I can't (?) make such a hash and even can't use strings (tag names) in a switch statement (with each branch calling a certain method). The possibility of using 10 or so subsequent ifs seems horribly ugly and I'm looking for a better way to code that.


Map string to a class instance by instantiating classes and saving them (probably in a hash). All the classes must implement the same interface of course.

You'll find that if you code this way a better structure starts to emerge from your code--for instance you might find that where before you might have used 2, 3 or 10 similar methods to do slightly different things, now the fact that you can pass data into your constructor allows you to do it all with one or two different classes instead.

This interface and the classes that implement it (for me at least) nearly always evolve into a full-featured set of classes that I needed all along but might not have recognized otherwise.

Somehow I never seem to regret writing code the "Hard" way, but nearly always regret when I choose the easier path.


What do people think of this?

public static enum Tags {
   TAG1, TAG2, TAG3
}

public class Stuff {
   ...
   switch (Tags.valueOf(str)) {
   case TAG1: handleTag1(); break;
   case TAG2: handleTag2(); break;
   case TAG3: handleTag3(); break;
   }
}

The upside is that this is concise and efficient (at least in this case). The downside is that it is not so good with mixed case tags and tags with Java non-identifier characters in them; e.g. "-". (You either have to abuse accepted Java style conventions for the enum member identifiers, or you have to add an explicit String-to-enum conversion method to the enum declaration.)

Using a switch statement for dispatching is evil in some peoples' book. But in this case, you need to compare what you are gaining with what you are loosing. And I'd be surprised if polymorphic dispatching would give a significant advantage over a switch statement in terms of extensibility and maintainability.


I'd go with what Bill K suggested in regards to implementing the same interface. But if you have the issue of wanting to call methods with different names you could try using reflection and do something like this:

Method method = Foo.class.getDeclaredMethod("methodName", parametersTypes); // Get the method you want to call
Foo foo = new Foo();
method.invoke(foo, args); // invoke the method retrieved on the object 'foo' with the given arguments


you can invoke the method using reflection:

Class.getMethod

therefore you don't need a switch or a set of ifs.


Here is an example of the proposal of Bill K (if I understood it right)

public class Example {

    static interface TagHandler {
        void handle(String tag);
    }

    static final Map<String, Example.TagHandler> tagHandlers = new HashMap<String, Example.TagHandler>() {
        {
            put("tag_1", new Example.TagHandler() {
                public void handle(String tag) {
                    System.out.println("Handling tag_1: " + tag);
                }
            });

            put("tag_2", new Example.TagHandler() {
                public void handle(String tag) {
                    System.out.println("Handling tag_2: " + tag);
                }
            });
        }
    };

    public static void main(String[] args) {

        String[] tags = { "tag_1", "tag_2", "tag_1" };

        for (String tag : tags) {
            tagHandlers.get(tag).handle(tag);
        }
    }
}


An indirect answer: XML typically represents data, not instructions. So it is probably more useful to map parser handling onto fields. This is what JAXB does. I suggest using JAXB or similar.

Unless you have a huge amount to do, I would strongly advise against reflection in a statically typed language. A string of } else if (tag.equals("blah")) { (or with interning, } else if (tag == "blah") { isn't going to kill you. You can even map strings onto their enum namesakes, but that is a little reflectiony. Switch-on-string should be with us in JDK7.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号