How to Call a Method Stored in a Hashmap? (Java)

How to call a method stored in a HashMap? (Java)

With Java 8+ and Lambda expressions

With lambdas (available in Java 8+) we can do it as follows:

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"
}
}

In this case I was lazy and reused the Runnable interface, but one could just as well use the Command-interface that I invented in the Java 7 version of the answer.

Also, there are alternatives to the () -> { ... } syntax. You could just as well have member functions for help and teleport and use YourClass::help resp. YourClass::teleport instead.

  • A great Lambda cheat sheet over at Programming.Guide.

  • Oracle tutorial here: The Java Tutorials™ – Lambda Expressions.


Java 7 and below

What you really want to do is to create an interface, named for instance Command (or reuse for instance Runnable), and let your map be of the type Map<Character, Command>. Like this:

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"

}
}

Reflection "hack"

With that said, you can actually do what you're asking for (using reflection and the Method class.)

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");
}
}

Store and call method from a HashMap

Here's one way I can think of, using the Consumer functional interface:

Map<String,Consumer<Object[]>> methods = new HashMap<>();

methods.put (USE_CASE_ONE, param -> ip.method1(param));
methods.put (USE_CASE_TWO, param -> ip.method2(param));
...

public void handle(String s, String param) {
methods.get(s).accept(new Object[]{param});
}

EDIT:

If your methods require two parameters, you can use the BiConsumer interface:

Map<String,BiConsumer<String,List<String>>> methods = new HashMap<>();

methods.put (USE_CASE_ONE, (s,l) -> ip.method1(s,l));
methods.put (USE_CASE_TWO, (s,l) -> ip.method2(s,l));
...

public void handle(String s, String param) {
methods.get(s).accept(someString,someListOfStrings);
}

How to call methods from inside of a hash map (something to do with the :: operator?)

The :: shows a method reference. The code you want would look something like this:

import java.util.function.Consumer;
...

class SomeClass {

public void doStuffA(String s) {some code A}
public void doStuffB(String s) {some code B}
public void doStuffC(String s) {some code C}

public void anotherMethod() {
HashMap<String, Consumer<String>> hmap = new HashMap<>();
hmap.put("Canada", this::doStuffA);
hmap.put("futon", this::doStuffB);
hmap.put("Peter", this::doStuffC);

String str = "pass in this string";
Consumer<String> runStuff = hmap.get("Peter");
runStuff.accept(str);
}
}

An explanation of how that code works:

The this::doStuffA above is a method reference to the doStuffA method that this has. If there is a different object called obj that has a method called doStuffA, you could just replace this::doStuffA with obj::doStuffA. If doStuffA is a static method, you can say ClassThatDoStuffAIsIn::doStuffA.

The Consumer interface looks something like this:

public interface Consumer<T> {
void accept(T t);
}

It is what is known as a functional interface. Functional interfaces are interfaces with a single abstract method. In this case, that method is accept.

Method references and lambda expressions are really just syntactic sugar. Java looks at the signature of your method (doStuffA) and the signature of the abstract method in the functional interface, and if it matches, it basically creates an object that overrides that abstract method by calling your method.

A method reference such as Consumer<String> c = this::doStuff or an equivalent lambda expression such as Consumer<String> c = (s) -> doStuff(c); really means something like this:

class ConsumerImpl implements Consumer<String> {
@Override //This annotation isn't actually added, but it's just for clarity
public void accept(String str) {
this.doStuffA(str);
}
}
...
Consumer<String> c = new ConsumerImpl();

or with an anonymous class:

Consumer<String> c = new Consumer<>() {
@Override //This annotation isn't actually added, but it's just for clarity
public void accept(String str) {
this.doStuffA(str);
}
};

This isn't actually what happens - the JVM treats such objects specially and uses an opcode called invokedynamic, but that's probably beyond the scope of this answer. For all intents and purposes, the above code is what a method reference expands to.

So you cannot just call these methods directly, like this:

Consumer<String> runStuff = hmap.get("Peter");
runStuff(str); //This is an error because there is no method called runStuff

Therefore, you have to use the accept method on the Consumer interface to actually use the doStuffA method.

EDIT: Just to clarify, you don't have to use the Consumer interface. You can make your own interface, named whatever you want, as long as it only has 1 abstract method (which you can also name whatever you want). This would also work:

public interface MyBeautifulFunctionalInterface {
void doStuffWithAString(String s);
}

and then

public static void main(String[] argh) {
MyBeautifulFunctionalInterface runStuff = this::doStuffA;
runStuff.doStuffWithAString("blah");
}

can i store function names in final hashmap for execution?

You could use reflection, but I suggest a easier way.

You can create an abstract class or interface with an abstract method execute. Example:

interface Command {
void execute(Object params[]);
}

class Help implements Command {
void execute(Object params[]) {
// do the stuff
}
}

Now your hashmap can be:

// creating the hasmap
HashMap<String,Command> myfunc = new HashMap<String,Command>();

// adding function reference
myfunc.put("help", new Help());

And then:

void receiveCommand(String command, Object params[]) {
myfunc.get(command).execute(params);
}

How to generically implement calling methods stored in a HashMap?

Regards to your code you didn't initiate method. Bear in mind that execute with null you must call public static method:

Your other issue , you didn't initiated interface properly. Here is working example:

InvokesMethodItf

public interface InvokesMethodItf {

public void invokeMethod() throws Exception;
public void setMethod(Method method);
}

InvokesMethod

public class InvokesMethod implements InvokesMethodItf{

private Method method;

@Override
public void invokeMethod() throws Exception {
method.invoke(null);
}

@Override
public void setMethod(Method method) {
this.method = method;
}

}

Terminal

public class Terminal {

public HashMap<Character, InvokesMethodItf> commands;

public Terminal() {
this.commands = new HashMap<Character, InvokesMethodItf>();

try {
this.setCommand('p',
this.getClass().getDeclaredMethod("printHelloWorld"));

} catch (Exception e) {
e.printStackTrace();
}
}

public static void printHelloWorld() {// method.invoke(null) looking for "static" method
System.out.println("Hello World!");
}

private void setCommand(char letter, Method method) {

InvokesMethodItf inv = new InvokesMethod();

inv.setMethod(method);

this.commands.put(letter, inv);
}

public void executeCommand(char letter) throws Exception {
this.commands.get(letter).invokeMethod();
}
}

Main

public class Main {
public static void main(String[] args) {
Terminal commandLine = new Terminal();

try {
commandLine.executeCommand('p');

} catch (Exception e) {
e.printStackTrace();
}
}
}

Output:

Hello World!

Calling a method of an Object in a HashMap in Java

If you may not use casts and instanceof and must use polymorphism, then add two methods to your CarEntity interface :

boolean canBeLoadedWithCars();
void addCar(CarEntity c) throws IllegalStateException;

The trucks can be loaded with cars, and thus implement the first methof by returning true. The other ones return false.

The addCar method of Truck adds the car to their map, whereas the other implementations throw an IllegalStateException because they can't be loaded with cars.

So the addCar method of the manager becomes

CarEntity truck = hashMap.get(truckId);
if (truck.canBeLoadedWithCars() {
truck.addCar(sedan);
}

java 8 - store method in HashMap and get return value from method in map

Runnable doesn't return a value. You should use Supplier or Callable instead.

The primary difference between Supplier and Callable is that Callable allows you to throw a checked exception. You then have to handle the possibility of that exception everywhere you use the Callable. Supplier is probably simpler for your use case.

You would need to change your Map<String, Runnable> to a Map<String, Supplier<Integer>>. The lambda function itself wouldn't need changing.

@assylias pointed out in a comment that you could also use Map<String, IntSupplier>. Using an IntSupplier avoids boxing your int as an Integer.

How to make a hashmap of dynamic methods in Java

All functions will take in string and return void

In this situation, you can use a Consumer<String> interface and create a factory as follows:

public class ConsumerFactory {

private static final Map<String, Consumer<String>> consumers = new HashMap<>();

public static Consumer<String> getConsumer(String key) {
if (key != null && consumers.containsKey(key)) {
return consumers.get(key);
}
throw new NoSuchElementException(key);
}

public static Consumer<String> addConsumer(String key, Consumer<String> value) {
return consumers.put(key, value);
}
}

ConsumerFactory.addConsumer("print", System.out::println);
ConsumerFactory.getConsumer("print").accept("Hello");

How to call the method of HashMap from another class?

Assessment is an abstract class so you will need to extend it if you want to use it:

class MyAssessment extends Assessment {
@Override
public String description() {
return "My assessment";
}
}

Then you can call addMark on this new implementation

MyAssessment myAssessment = new MyAssessment();
myAssessment.addMark("11", m1);

Also this method throws an Exception so you need to try/catch it

try {
new MyAssessment().addMark("11", m1);
} catch(Exception e) {
e.printStackTrace();
}

And finally you have no weight property in your Assessment class so you should somehow define one if you want the class to compile.

private int weight = 0;

Call a method contained in the class that is the hashmap value (Java)

Presumably you're simply looking for...

for (LibraryItem libraryItem : itemMap.values()) {
libraryItem.printDetails();
}

The for (T varName : collection) {} construct can be used to loop over any collection; itemMap.values() returns a collection. It's not an ArrayList, but that's fine.



Related Topics



Leave a reply



Submit