2016-06-04 4 views
2

Kann Java 8 Consumer irgendwie parametrisiert werden? Ich möchte einen wiederverwendbaren Verbraucher haben, bei dem ich zusätzliche Argumente an die Stelle setzen kann, an denen ich ihn verwende.Java 8 funktionale Schnittstelle (Consumer) zusätzlicher Parameter

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 sind vom gleichen Typ

Wie Sie sowohl Verbraucher nur in einem Argument unterscheiden sehen. Ist es möglich, diesen Code zu vereinfachen/nicht zu duplizieren?

Antwort

3

erstellen Factory-Methode für Ihre Kunden:

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

Dann ist es wie folgt verwenden:

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

Aber auch: Es schlechte Praxis (gegen funktionale Programmierung Prinzipien) haben ein Lambda-Ausdruck oder eine Lambda-Funktion mit Nebeneffekten, z. B. das Hinzufügen von Alarmen zu der Alarmgruppe oder das Hinzufügen von Daten zur Liste der Daten innerhalb des Lambda. Ein funktionellerer Ansatz wäre es, Transformationsmethoden wie map zu verwenden und dann die Ergebnisse zu sammeln. Zum Beispiel:

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

Während' collect' besser ist, hat Java keine gute Möglichkeit, zwei Ergebnisse zu erzeugen, dh "Alarme" und "Daten" aus einem Stream. –

+2

@Jesper Während "funktionale Programmierungsprinzipien" von der Abhängigkeit von Nebenwirkungen abraten, beachten Sie bitte, dass die funktionale Schnittstelle 'Consumer ' _ ausschließlich für den Zweck der Modellierung von seiteneffektiven Berechnungen existiert. Es gibt nichts zurück - wenn es also eine reine Funktion darstellt, wäre die Abstraktion nutzlos. (Es ist der Fall, dass Stream-Methoden wie 'forEach()' oft verwendet werden, wenn 'collect()' oder 'reduce()' den Job besser machen könnten - aber vorausgesetzt, die Wahl, 'Consumer' zu verwenden, war Sound keinen Sinn, dann die Nebenwirkungen zu kritisieren, die der Implementierung von 'Consumer' innewohnen.) –

+0

@BrianGoetz danke für den Kommentar, ich verstehe, ich wollte das erwähnen, weil ich oft Leute ohne funktionale Programmiererfahrung' forEach' mit Nebeneffekt sehe Lambda für alles, anstatt "den funktionalen Weg" zu denken. – Jesper

2

Wir ändern die API, obwohl dies hier keine einfache Option ist.

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

und rufen Sie die API wie folgt (wobei das erste Argument für jeden Aufruf entryConsumer übergeben wird)

.forEach(calendar1.getPattern, entryConsumer); 

jedoch sagen, dass Sie nicht die API ändern können, was Sie tun können, ist die Verwendung eine Methode wie diese.

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

Ich würde die "externe" Methode bevorzugen, aber Sie können auch 'BiConsumer' verwenden, ohne die API zu ändern, da es gilt:' .forEach (e -> entryConsumer.accept (calendar1, e)) ' – zapl

Verwandte Themen