0

I have this method that calls multiple APIs and aggregates all the results and returns them in a Map where each set of results is matched to the target API so that they can be grouped and displayed properly in a service.

public AggregateResults search(QueryParams params) {
    Stream<APITarget> targets = params.getTargets().parallelStream();

    Function<APITarget, APIResults> func = (APITarget target) -> {
        if (params.length() >= MIN_LENGTH) {
           switch (target) {
               case api1:
                    return searchAPI1(params);
               case api2:
                   return searchAPI2(params);
               .
               .
               case apin:
                    return searchAPIn(params);
            }
        }
        return new APIResults.Builder().build();
    };

    Map<APITarget, APIResults> results = targets.collect(Collectors.toMap(t -> t, func));
    return AggregateResults;
}

I have now have to refactor this code so that for example API 1 - 3 can be called in one function and then API 4 - N can be called from another function. The functions will be called from different methods so I need to move this function out of this method and then pass in the QueryParams object in as another parameter to the function but then I run into the issue of the Function not being able to accept more than one parameter. E.g.

Function<APITarget, APIResults> exampleFunc = (APITarget target, QueryParams params) -> {
    if (params.length() >= MIN_LENGTH) {
        switch (target) {
            case api1:
                return searchAPI1(params);
            case api2:
                return searchAPI2(params);            
        }
    }
    return new APIResults.Builder().build();
};

I have seen something similar mentioned here: Can a java lambda have more than 1 parameter? But the examples of BiFunctions only show them being used as lambdas and so wont work when being called by an external method, from what I have been able to find. Also when it talks about creating a new FunctionalInterface I'm not sure what exactly is needed to be able to use the new interface in my required functions.

Any ideas would be great.

Anto
  • 419
  • 2
  • 9
  • 19
  • What's wrong with `BiFunction`? "...only show them being used as lambdas and so wont work when being called by an external method" - I don't understand that part of your question. – Sartorius Jul 05 '19 at 17:57
  • This example is a lambda `BiFunction function1 = (s1, s2) -> { String s3 = s1 + s2; return s3; };` I need to be able to define the function in the class and then call it from inside two different methods with something like `Map results = targets.collect(Collectors.toMap(t -> t, func(t,params)));` – Anto Jul 06 '19 at 06:18

2 Answers2

1

Modified to reflect example

First, define some required BiFunction


  BiFunction<Integer, int[], Integer> func = (a, b) ->
      {
         for (int i = 0; i < b.length; i++) {
            a *= b[i];
         }
         return Integer.valueOf(a);
      };


Now define some data.

      //Stream source
      int[] data = { 10, 20, 30
      };
      // parameter source
      int[] params = { 3, 5, 11
      };
      Convert data to stream
      IntStream targets = Arrays.stream(data);

Now apply the mapping function.


     Map<Integer, Integer> results = targets.boxed().collect(
            Collectors.toMap(t -> t, t -> func.apply(t, params)));
      System.out.println(results);

The above takes the stream and creates a map with the data acting as the keys and then takes those same keys and applies them to function with the parameter list. That becomes the value of the map.

You will probably need to use mapToObj or map in place of the boxed() method above depending on the initial stream type.

WJS
  • 36,363
  • 4
  • 24
  • 39
  • Yes but without the lambda execution straight after defining the function, I've added an example in the comments above – Anto Jul 06 '19 at 07:09
  • @Anto I modified my answer based on your example. Still not certain if that's what you want. – WJS Jul 06 '19 at 15:43
  • Thanks @WJS! I thought I had tried that but I obviously had something wrong because it was either complaining about the params going into the function or complaining that the a "method call is expected" – Anto Jul 07 '19 at 19:20
1

A functional interface is (in simple terms) an interface with one method.

You'd want to annotate the interface with @FunctionalInterface to document its purpose, and to ensure nobody accidentally adds another method.

The name of the method doesn't really matter, but should of course be appropriate for what it's supposed to do.

So, if you need a function with 3 parameters, create your own functional interface. You can make it specific or or make it general purpose using generic type arguments.

@FunctionalInterface
interface TripleString {
    String doStringOp(String a, String b, String c);
}

@FunctionalInterface
interface TriFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • I think my main issue is that after defining a FuinctionalInterface like above and then defining my function like `TriFunction trifunc = (SearchTarget target, QueryParams params, SearchResults results) -> {... return apiresults };` or using a BiFunction, I cannot figure out how to the use that in the map like `Map results = targets.collect(Collectors.toMap(t -> trifunc(t,params,null)));` Maybe functions can only be used in lambdas like in the answer below – Anto Jul 06 '19 at 07:07