Search code examples
javalambdajava-8functional-interface

Java 8 functional interface (Consumer) additional parameter


Is it possible to somehow parametrize Java 8 Consumer? I want to have reusable Consumer where I can put additional arguments in place where I use it.

List<DateTime> dates = new ArrayList<DateTime>();
Set<Alarm> alarms = new HashSet<Alarm>();

Consumer<Entry> entryConsumer1 = entry -> {
    LocalTime time = entry.getDate().toLocalTime();
    Alarm alarm = new Alarm(time, calendar1.getPattern());
    alarms.add(alarm);
    dates.add(entry.getDate());
};

Consumer<Entry> entryConsumer2 = entry -> {
    LocalTime time = entry.getDate().toLocalTime();
    Alarm alarm = new Alarm(time, calendar2.getPattern());
    alarms.add(alarm);
    dates.add(entry.getDate());
};

calendar1.generateEntries(criteria).forEach(entryConsumer1);
calendar2.generateEntries(criteria).forEach(entryConsumer2);

calendar1, calendar2 are the same type

As you can see both consumers differ only in one argument. Is it possible to simplify this code/don't duplicate?


Solution

  • Create a factory method for your consumers:

    public Consumer<Entry> createConsumer(Calendar calendar, Set<Alarm> alarms, List<DateTime> dates) {
        return entry -> {
            LocalTime time = entry.getDate().toLocalTime();
            Alarm alarm = new Alarm(time, calendar.getPattern());
            alarms.add(alarm);
            dates.add(entry.getDate());
        }
    }
    

    Then use it like this:

    calendar1.generateEntries(criteria).forEach(createConsumer(calendar1, alarms, dates));
    calendar2.generateEntries(criteria).forEach(createConsumer(calendar2, alarms, dates));
    

    But also: It is bad practice (against functional programming principles) to have a lambda expression or function with side effects, such as adding alarms to the set of alarms or adding dates to the list of dates inside the lambda. A more functional approach would be to use transformation methods like map and then collect the results. For example:

    Set<Alarm> alarms = calendar1.generateEntries(criteria)
        .map(entry -> new Alarm(entry.getDate().toLocalTime(), calendar1.getPattern()))
        .collect(Collectors.toSet());