web-dev-qa-db-fra.com

Comment appeler une méthode stockée dans un HashMap? (Java)

J'ai une liste de commandes (i, h, t, etc.) que l'utilisateur entrera sur une ligne de commande/terminal Java. Je voudrais stocker un hachage de commande/méthode paires:

'h', showHelp()
't', teleport()

Pour que je puisse avoir du code comme:

HashMap cmdList = new HashMap();

cmdList.put('h', showHelp());
if(!cmdList.containsKey('h'))
    System.out.print("No such command.")
else
   cmdList.getValue('h')   // This should run showHelp().

Est-ce possible? Sinon, quelle est la manière la plus simple de procéder?

50
cwhiii

Avec Java 8+ et expressions Lambda

Avec lambdas (disponible en Java 8+) nous pouvons le faire comme suit:

class Test {

    public static void main(String[] args) throws Exception {
        Map<Character, Runnable> commands = new HashMap<>();

        // Populate commands map
        commands.put('h', () -> System.out.println("Help"));
        commands.put('t', () -> System.out.println("Teleport"));

        // Invoke some command
        char cmd = 't';
        commands.get(cmd).run();   // Prints "Teleport"
    }
}

Dans ce cas, j'étais paresseux et j'ai réutilisé l'interface Runnable, mais on pourrait tout aussi bien utiliser l'interface Command- que j'ai inventée dans la version Java 7 de la réponse.

Il existe également des alternatives au () -> { ... } syntaxe. Vous pourriez tout aussi bien avoir des fonctions membres pour help et teleport et utiliser YourClass::help resp. YourClass::teleport au lieu.


Java 7 et inférieur

Ce que vous voulez vraiment faire, c'est créer une interface, nommée par exemple Command (ou réutiliser par exemple Runnable), et laissez votre carte être du type Map<Character, Command>. Comme ça:

import Java.util.*;

interface Command {
    void runCommand();
}

public class Test {

    public static void main(String[] args) throws Exception {
        Map<Character, Command> methodMap = new HashMap<Character, Command>();

        methodMap.put('h', new Command() {
            public void runCommand() { System.out.println("help"); };
        });

        methodMap.put('t', new Command() {
            public void runCommand() { System.out.println("teleport"); };
        });

        char cmd = 'h';
        methodMap.get(cmd).runCommand();  // prints "Help"

        cmd = 't';
        methodMap.get(cmd).runCommand();  // prints "teleport"

    }
}

Réflexion "hack"

Cela dit, vous pouvez réellement faire ce que vous demandez (en utilisant la réflexion et la classe Method.)

import Java.lang.reflect.*;
import Java.util.*;

public class Test {

    public static void main(String[] args) throws Exception {
        Map<Character, Method> methodMap = new HashMap<Character, Method>();

        methodMap.put('h', Test.class.getMethod("showHelp"));
        methodMap.put('t', Test.class.getMethod("teleport"));

        char cmd = 'h';
        methodMap.get(cmd).invoke(null);  // prints "Help"

        cmd = 't';
        methodMap.get(cmd).invoke(null);  // prints "teleport"

    }

    public static void showHelp() {
        System.out.println("Help");
    }

    public static void teleport() {
        System.out.println("teleport");
    }
}
101
aioobe

Bien que vous puissiez stocker des méthodes par réflexion, la façon habituelle de le faire est d'utiliser des objets anonymes qui enveloppent la fonction, c'est-à-dire.

  interface IFooBar {
    void callMe();
  }


 'h', new IFooBar(){ void callMe() { showHelp(); } }
 't', new IFooBar(){ void callMe() { teleport(); } }

 HashTable<IFooBar> myHashTable;
 ...
 myHashTable.get('h').callMe();
5
Erich Kitzmueller

Si vous utilisez JDK 7, vous pouvez désormais utiliser des méthodes par expression lambda tout comme .net.

Sinon, la meilleure façon est de créer un objet fonction:

public interface Action {    void performAction(); }

Hashmap<string,Action> cmdList;

if(!cmdList.containsKey('h'))
    System.out.print("No such command.") else    cmdList.getValue('h').performAction();
4
DVD