Java 8 додала концепцію функціональних інтерфейсів , а також численні нові методи, розроблені для прийняття функціональних інтерфейсів. Екземпляри цих інтерфейсів можуть бути створені лаконічно, використовуючи вирази опорних методів (наприклад SomeClass::someMethod
) та лямбда-вирази (наприклад (x, y) -> x + y
).
Ми з колегою маємо різні думки щодо того, коли найкраще використовувати ту чи іншу форму (де "найкраще" в цьому випадку дійсно зводиться до "найбільш читабельного" і "найбільш відповідного стандартним практикам", оскільки вони в основному інакше еквівалент). Зокрема, це стосується випадку, коли всі справжні:
- відповідна функція не використовується поза однією областю
- давання імені примірника допомагає читати (на відміну, наприклад, якщо логіка є достатньо простою, щоб побачити, що відбувається з першого погляду)
- немає інших причин програмування, чому одна форма була б кращою для іншої.
Моя сьогоднішня думка з цього приводу полягає в тому, що кращим підходом є додавання приватного методу та посилання на це шляхом посилання на метод. Складається враження, що саме така функція була розроблена для використання, і, здається, простіше повідомляти про те, що відбувається через назви методів та підписи (наприклад, "булева isResultInFuture (результат результату)" чітко говорить про повернення булева). Це також робить приватний метод більш багаторазовим, якщо майбутні розширення класу хочуть використовувати ту саму перевірку, але не потребує обгортки функціонального інтерфейсу.
Перевагою мого колеги є використання методу, який повертає примірник інтерфейсу (наприклад, "Predicate resultInFuture ()"). Мені здається, що це не зовсім те, як функція була призначена для використання, відчуває трохи незграбність і здається, що важче реально спілкуватися про наміри через іменування.
Щоб зробити цей приклад конкретним, ось той самий код, написаний у різних стилях:
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
});
}
}
Чи є якась офіційна або напівофіційна документація чи коментарі навколо того, чи є один підхід більш кращим, більш відповідає намірам дизайнерів мови чи більше читається? Якщо є офіційне джерело, чи є чіткі причини, чому можна було б зробити кращий підхід?
Object::toString
). Тож моє питання стосується того, чи краще в цьому конкретному типі екземплярів, які я тут викладаю, ніж чи існують випадки, коли один кращий за інший, чи навпаки.