Kha Nguyễn
Kha Nguyễn

Reputation: 463

Use HashMap<String, Runnable> to avoid duplicate method

Greeting, I have some functions defined like :

@RequestMapping(value = "/getWeek", method = RequestMethod.GET)
public ResponseEntity<?> getAvgWeek(BigInteger id) {
    List<TimePeriodCalc> result = Calc.getWeek(id);
    return new ResponseEntity<>(result, HttpStatus.OK);
}

And

@RequestMapping(value = "/getMonth", method = RequestMethod.GET)
public ResponseEntity<?> getAvgMonth(BigInteger id) {
    List<TimePeriodCalc> result = Calc.getMonth(id);
    return new ResponseEntity<>(result, HttpStatus.OK);
}

And maybe getYear, getQuarter, getDate ....

Now I would like to combine them and add one parameter to tell me what I need to return. The problem now is I have to define switch case statement which may make my code lengthy and unprofessional. Is there anyway I can use Lambda expression like this case :

HashMap<String, Runnable> myMap = .... (declare and input some values)
myMap.put("executeA", () -> funA())
myMap.get("executeA")

Then my code would look like :

result = myMap.get("week"); 
//Or any kinds of assignment, the map does not have to return a list

or :

result = myMap.get("year");

and so on ...

Thank you.

Upvotes: 2

Views: 829

Answers (1)

tobias_k
tobias_k

Reputation: 82899

Assuming that the getWeek, getMonth etc. methods all take the same single parameter (a BigInteger, according to your comment) you can create a Map mapping strings to Function<BigInteger, List<TimePeriodCalc>> and populate this map with static method references...

Map<String, Function<BigInteger, List<TimePeriodCalc>>> myMap = new HashMap<>();
myMap.put("week", Calc::getWeek);
myMap.put("month", Calc::getMonth)
...

... and then get the proper function from the map and apply it in your generic method:

@RequestMapping(value = "/getAny", method = RequestMethod.GET)
public ResponseEntity<?> getAvgAny(String period, BigInteger id) {
    List<TimePeriodCalc> result = myMap.get(period).apply(id)
    return new ResponseEntity<>(result, HttpStatus.OK);
}

Concerning your comment: Java only has Function for functions with one parameter, and BiFunction for two parameters, i.e. x -> y and (x, y) -> z. AFAIK functions or method references with more than two parameters are not possible. If you have to pass more than two parameters, you could, as a workaround, use a Supplier, taking no parameters but accessing the variables in its scope. This, however, would require the Map to be defined within the method:

@RequestMapping(value = "/getAny", method = RequestMethod.GET)
public ResponseEntity<?> getAvgAny(String period, BigInteger id, many other parameters) {    
    Map<String, Supplier<List<TimePeriodCalc>>> myMap = new HashMap<>();
    myMap.put("week",  () -> Calc.getWeek(id and many other parameters));
    ...
    List<TimePeriodCalc> result = myMap.get(period).get();
    return new ResponseEntity<>(result, HttpStatus.OK);
}

Or you might wrap the different parameters into an Object[] and pass that to the function:

Map<String, Function<Object[], List<TimePeriodCalc>>> myMap = new HashMap<>();
myMap.put("week",  p -> Calc.getWeek((BigInteger) p[0], (Some) p[1], (Other) p[2], (Class) p[3]));
...

@RequestMapping(value = "/getAny", method = RequestMethod.GET)
public ResponseEntity<?> getAvgAny(String period, BigInteger id, many other parameters) {    
    Object[] params = new Object[] {id and  many other parameters};
    List<TimePeriodCalc> result = myMap.get(period).apply(params);
    return new ResponseEntity<>(result, HttpStatus.OK);
}

As pointed out by Holger in comments, you can also define your own interface providing a single function with more than two parameters and then create a Map of those, using a lambda to implement that method and mapping from those parameters to the function you want to call.

interface MyInterface {
    List<TimePeriodCalc> getResult(BigInteger id, some more stuff);
}

Map<String, MyInterface> myMap = new HashMap<>();
myMap.put("week", (id, more, stuff) -> Calc.getweek(id, more, stuff));
...
List<TimePeriodCalc> test = myMap.get("week").getResult(id, more, stuff);

Upvotes: 3

Related Questions