Java 8 ha aggiunto il concetto di interfacce funzionali , nonché numerosi nuovi metodi progettati per assumere interfacce funzionali. Le istanze di queste interfacce possono essere create in modo succinto usando espressioni di riferimento del metodo (ad es. SomeClass::someMethod
) Ed espressioni lambda (ad es (x, y) -> x + y
.).
Un collega e io abbiamo opinioni diverse su quando sia meglio usare una forma o l'altra (dove "migliore" in questo caso si riduce davvero a "più leggibile" e "la maggior parte in linea con le pratiche standard", dato che sostanzialmente sono altrimenti equivalente). Nello specifico, ciò implica il caso in cui sono vere tutte le seguenti condizioni:
- la funzione in questione non viene utilizzata al di fuori di un singolo ambito
- dare un nome all'istanza aiuta la leggibilità (al contrario, ad esempio, della logica è abbastanza semplice da vedere cosa sta succedendo a colpo d'occhio)
- non ci sono altri motivi di programmazione per cui un modulo sarebbe preferibile all'altro.
La mia attuale opinione in merito è che l'aggiunta di un metodo privato e il riferimento a tale metodo sia l'approccio migliore. Sembra che sia così che la funzionalità è stata progettata per essere utilizzata e sembra più facile comunicare cosa sta succedendo tramite nomi e firme dei metodi (ad es. "Booleano isResultInFuture (Risultato del risultato)" sta chiaramente dicendo che sta restituendo un valore booleano). Inoltre, rende il metodo privato più riutilizzabile se un miglioramento futuro della classe desidera utilizzare lo stesso controllo, ma non necessita del wrapper di interfaccia funzionale.
La preferenza del mio collega è di avere un metodo che restituisca l'istanza dell'interfaccia (es. "Predicate resultInFuture ()"). Per me, sembra che non sia proprio il modo in cui la funzionalità è stata progettata per essere utilizzata, sembra leggermente più clunkier e sembra che sia più difficile comunicare realmente l'intenzione attraverso la denominazione.
Per rendere concreto questo esempio, ecco lo stesso codice, scritto nei diversi stili:
public class ResultProcessor {
public void doSomethingImportant(List<Result> results) {
results.filter(this::isResultInFuture).forEach({ result ->
// Do something important with each future result line
});
}
private boolean isResultInFuture(Result result) {
someOtherService.getResultDateFromDatabase(result).after(new Date());
}
}
vs.
public class ResultProcessor {
public void doSomethingImportant(List<Result> results) {
results.filter(resultInFuture()).forEach({ result ->
// Do something important with each future result line
});
}
private Predicate<Result> resultInFuture() {
return result -> someOtherService.getResultDateFromDatabase(result).after(new Date());
}
}
vs.
public class ResultProcessor {
public void doSomethingImportant(List<Result> results) {
Predicate<Result> resultInFuture = result -> someOtherService.getResultDateFromDatabase(result).after(new Date());
results.filter(resultInFuture).forEach({ result ->
// Do something important with each future result line
});
}
}
Esistono documenti o commenti ufficiali o semi-ufficiali sul fatto che un approccio sia più preferito, più in linea con le intenzioni dei progettisti del linguaggio o più leggibile? Escludendo una fonte ufficiale, ci sono dei motivi chiari per cui uno sarebbe l'approccio migliore?
Object::toString
). Quindi la mia domanda è più se è meglio in questo particolare tipo di istanza che sto ponendo qui, che se esistono casi in cui uno è migliore dell'altro, o viceversa.