Java 8 dodała koncepcję funkcjonalnych interfejsów , a także wiele nowych metod, które zostały zaprojektowane do przyjmowania funkcjonalnych interfejsów. Wystąpienia tych interfejsów można zwięźle utworzyć za pomocą wyrażeń referencyjnych metod (np. SomeClass::someMethod
) I wyrażeń lambda (np (x, y) -> x + y
.).
Wspólnie z koleżanką mamy różne opinie na temat tego, kiedy najlepiej użyć takiej lub innej formy (gdzie „najlepsze” w tym przypadku naprawdę sprowadza się do „najbardziej czytelnych” i „najbardziej zgodnych ze standardowymi praktykami”, ponieważ zasadniczo są inaczej odpowiednik). W szczególności dotyczy to przypadku, gdy spełnione są następujące warunki:
- funkcja, o której mowa, nie jest używana poza jednym zakresem
- nadanie instancji nazwy poprawia czytelność (w przeciwieństwie do np. logiki, która jest wystarczająco prosta, aby zobaczyć, co się dzieje na pierwszy rzut oka)
- nie ma innych powodów programowania, dla których jedna forma byłaby lepsza od drugiej.
Moja obecna opinia w tej sprawie jest taka, że dodanie metody prywatnej i odwołanie się do niej poprzez odniesienie do metody jest lepszym podejściem. Wygląda na to, że właśnie w ten sposób zaprojektowano tę funkcję i łatwiej jest komunikować o tym, co się dzieje za pomocą nazw metod i podpisów (np. „Boolean isResultInFuture (wynik wyniku)” wyraźnie mówi, że zwraca wartość logiczną). Powoduje to również, że metoda prywatna jest bardziej przydatna, jeśli przyszłe rozszerzenie klasy chce skorzystać z tej samej kontroli, ale nie potrzebuje funkcjonalnego opakowania interfejsu.
Mój kolega preferuje metodę, która zwraca instancję interfejsu (np. „Predicate resultInFuture ()”). Dla mnie wydaje się, że nie jest to dokładnie tak, jak ta funkcja była przeznaczona do użycia, wydaje się nieco dziwniejsza i wydaje się, że trudniej jest naprawdę komunikować zamiar poprzez nazewnictwo.
Aby uczynić ten przykład konkretnym, oto ten sam kod napisany w różnych stylach:
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
});
}
}
Czy istnieje jakaś oficjalna lub półoficjalna dokumentacja lub komentarze dotyczące tego, czy jedno podejście jest bardziej preferowane, bardziej zgodne z intencjami projektantów języków, czy bardziej czytelne? Czy poza oficjalnym źródłem istnieją wyraźne powody, dla których lepiej byłoby podejść?
Object::toString
.). Moje pytanie dotyczy więc bardziej tego, czy lepiej jest w tym konkretnym typie instancji, który tu przedstawiam, niż czy istnieją sytuacje, w których jedna jest lepsza od drugiej, lub odwrotnie.