Home > Enterprise >  Passing Supplier instead of Function as argument in java 8
Passing Supplier instead of Function as argument in java 8

Time:07-03

I want to filter out duplicates on the basis of some attributes in Person class. I have a method definition as below :

 private static <T> Predicate<T> distinctByKeys(final Function<? super T, ?>... keyExtractors) 
{
  final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
 
return t -> 
{
  final List<?> keys = Arrays.stream(keyExtractors)
              .map(ke -> ke.apply(t))
              .collect(Collectors.toList());
   
  return seen.putIfAbsent(keys, Boolean.TRUE) == null;
  };
 }

And the call to above method is made like this:

    List<Person> distinctPersons = list.stream()
      .filter(distinctByKeys(Person::getFirstName, Person::getLastName))
      .collect(Collectors.toList());

In the method definition i can see the arguments can be Function implementations but the parameters passed in the above code are Supplier type (as they are returning a result and not taking any arg. But this works fine. How is it working can anyone explain.

CodePudding user response:

If you use a method reference using Type::method, where method is non-static, there is an explicit argument for the object to call the method on. In your case, Person::getFirstName is similar to (Person p) -> p.getFirstName().

CodePudding user response:

the parameters passed in the above code are Supplier type (as they are returning a result and not taking any arg. But this works fine. How is it working can anyone explain.

In the following code:

.filter(distinctByKeys(Person::getFirstName, Person::getLastName))

Method references Person::getFirstName and Person::getLastName are not of type Supplier, these are Funtions because they do require an argument of type Person in order to return a person's first name or last name.

CodePudding user response:

In the first example I simply assigned distinctByKeys and forSupplier methods to Function interface. Then I just apply(execute) method with apply method by given parameters.

Also supplier can be used as you can see from the second example. Last example is yours. But I have a question.

Map instantionation in the distinctByKeys method is a little bit suspicious. I just can imagine that map is instantiated when each person is called by filter method so It means that it will return true each time because map is empty and there is no keys in the map which is queried by seen.putIfAbsent(keys, Boolean.TRUE) method call.

private static Predicate functionExample() {
    Function<Function[], Predicate> f = Scratch::distinctByKeys;
    Function<Supplier[], Predicate> f2 = Scratch::forSupplier;
    f2.apply(new Supplier[0]);
    return f.apply(new Function[0]);
}


private static <T> Predicate<T> forSupplier(final Supplier<? super T>... suppliers) {
    final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();

    return t ->
    {
        final List<?> keys = Arrays.stream(suppliers)
                .map(Supplier::get)
                .collect(Collectors.toList());

        return seen.putIfAbsent(keys, Boolean.TRUE) == null;
    };
}

private static <T> Predicate<T> distinctByKeys(final Function<? super T, ?>... keyFields)
{
    final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();

    return t ->
    {
        final List<?> keys = Arrays.stream(keyFields)
                .map(ke -> ke.apply(t))
                .collect(Collectors.toList());

        return seen.putIfAbsent(keys, Boolean.TRUE) == null;
    };
}
  • Related