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
How to Crop a Bitmap for Imageview
Could Not Find Method in Parent or Ancestor Context
Multidex Noclassdeffound Error
Android: Why Setvisibility(View.Gone); or Setvisibility(View.Invisible); Do Not Work
How to Avoid Unnecessary Firestore Reads with Cache
Open Source Face Recognition for Android
Adding Header to All Request with Retrofit 2
What's the Best Practice to Round a Float to 2 Decimals
Android Drawable Speech Bubble
Android 3.1 Usb-Host - Broadcastreceiver Does Not Receive Usb_Device_Attached
Gson Expected Begin_Array But Was Begin_Object
Onintercepttouchevent Only Gets Action_Down
Android Httpclient Persistent Cookies