2016-06-04 3 views
2

Возможно ли каким-либо образом параметризовать Java 8 Consumer? Я хочу иметь многоразового Потребителя, где я могу добавить дополнительные аргументы, где я его использую.Дополнительный параметр функционального интерфейса Java 8 (потребитель)

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 тот же самый тип

Как вы можете видеть, как потребители отличаются только один аргумент. Можно ли упростить этот код/​​не дублировать?

ответ

3

Создать фабричный метод для потребителей:

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()); 
    } 
} 

Затем использовать его как это:

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

Но также: Это плохая практика (против принципов функционального программирования), чтобы иметь лямбда-выражение или функция с побочными эффектами, например добавление сигналов тревоги к набору тревог или добавление дат в список дат внутри лямбда. Более функциональный подход заключается в использовании методов преобразования, таких как map, а затем для сбора результатов. Например:

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

При использовании' collect' лучше, Java не имеет хорошего способа создания двух результатов, т.е. 'alarms' и' date' из одного потока. –

+2

@ Jesper В то время как «принципы функционального программирования» препятствуют зависимости от побочных эффектов, обратите внимание, что функциональный интерфейс «Потребитель» '_exists исключительно для цели моделирования побочных вычислений. Он ничего не возвращает - поэтому, если он представляет собой чистую функцию, абстракция будет бесполезной. (Это так, что методы потока, такие как 'forEach()', часто используются, когда 'collect()' или 'reduce()' может лучше выполнять работу, но если предположить, что выбор использовать «Потребитель» был прав, он делает нет смысла критиковать побочные эффекты, присущие внедрению «Потребителя».) –

+0

@BrianGoetz благодарит за комментарий, я понимаю, я хотел упомянуть об этом, потому что я часто вижу, что люди без функционального программирования используют 'forEach' с побочными эффектами lambdas для всего, вместо того, чтобы думать «функциональный путь». – Jesper

2

Что мы делаем, это изменить API, хотя здесь это не простой вариант.

BiConsumer<String, Entry> entryConsumer = (pattern, entry) -> { 
    LocalTime time = entry.getDate().toLocalTime(); 
    Alarm alarm = new Alarm(time, pattern); 
    alarms.add(alarm); 
    dates.add(entry.getDate()); 
}; 

и вызвать API, как это (где первый аргумент передается в каждом вызове entryConsumer)

.forEach(calendar1.getPattern, entryConsumer); 

Однако, говорят, что вы не можете изменить API, что вы можете сделать, это использовать такой метод.

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()); 
}; 
+3

Я предпочел бы «внешний» метод, но вы также можете использовать 'BiConsumer' без изменения API, так как она имеет один:' .forEach (е -> entryConsumer.accept (Calendar1, е)) ' – zapl

Смежные вопросы