Reputation: 2209
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?
Upvotes: 3
Views: 3570
Reputation: 206786
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());
Upvotes: 5
Reputation: 533492
What we do is change the API, though it is not a simple option here.
BiConsumer<String, Entry> entryConsumer = (pattern, entry) -> {
LocalTime time = entry.getDate().toLocalTime();
Alarm alarm = new Alarm(time, pattern);
alarms.add(alarm);
dates.add(entry.getDate());
};
and call the API like this (where the first argument is passed to each call to entryConsumer)
.forEach(calendar1.getPattern, entryConsumer);
However, say you can't change the API, what you can do is use a method like this.
calendar1.generateEntries(criteria).forEach(e -> entryConsumer(calendar1, e));
calendar2.generateEntries(criteria).forEach(e -> entryConsumer(calendar2, e));
public static void entryConsumer(Calendar cal, Entry e) {
LocalTime time = entry.getDate().toLocalTime();
Alarm alarm = new Alarm(time, cal.getPattern());
alarms.add(alarm);
dates.add(entry.getDate());
};
Upvotes: 4