Java 8 ha aggiunto il concetto di interfacce funzionali , così come numerosi nuovi metodi che sono progettati per prendere interfacce funzionali. Le istanze di queste interfacce possono essere create in modo succinto usando espressioni di riferimento al metodo (ad esempio SomeClass::someMethod
) e espressioni lambda (es. (x, y) -> x + y
).
Un collega e io abbiamo opinioni divergenti su quando è meglio usare una forma o l'altra (dove "il migliore" in questo caso si riduce davvero a "più leggibile" e "più in linea con le pratiche standard", come loro " fondamentalmente altrimenti equivalente). In particolare, questo riguarda il caso in cui i seguenti sono tutti veri:
- la funzione in questione non viene utilizzata al di fuori di un singolo ambito
- dare all'istanza un nome aiuta la leggibilità (al contrario, ad esempio, se la logica è abbastanza semplice da vedere cosa sta succedendo a colpo d'occhio)
- non ci sono altri motivi di programmazione per cui una forma sarebbe preferibile all'altra.
La mia opinione corrente sulla questione è che l'aggiunta di un metodo privato e il riferimento al metodo di riferimento sono l'approccio migliore. Sembra che questo sia il modo in cui la funzione è stata progettata per essere utilizzata e sembra più semplice comunicare ciò che sta accadendo tramite i nomi dei metodi e le firme (ad esempio "boolean isResultInFuture (risultato risultato)" sta chiaramente dicendo che sta restituendo un valore booleano). Rende inoltre più riutilizzabile il metodo privato se un miglioramento futuro della classe vuole utilizzare lo stesso controllo, ma non ha bisogno del wrapper dell'interfaccia funzionale.
La preferenza del mio collega è di avere un metodo che restituisce l'istanza dell'interfaccia (ad esempio "Predicate resultInFuture ()"). Per me, sembra che non sia proprio il modo in cui la funzione è stata pensata per essere usata, si sente leggermente più clamorosa e sembra che sia più difficile comunicare realmente l'intento 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 gli intenti dei designer linguistici o più leggibile? Escludendo una fonte ufficiale, ci sono motivi evidenti per cui uno sarebbe l'approccio migliore?