В Java 8 добавлена концепция функциональных интерфейсов , а также множество новых методов, предназначенных для использования функциональных интерфейсов. Экземпляры этих интерфейсов могут быть кратко созданы с использованием выражений ссылки на метод (например SomeClass::someMethod
) и лямбда-выражений (например (x, y) -> x + y
).
У нас с коллегой разные мнения о том, когда лучше использовать ту или иную форму (где «лучший» в данном случае действительно сводится к «наиболее читабельным» и «наиболее в соответствии со стандартной практикой», поскольку они в основном иным образом эквивалент). В частности, это касается случая, когда все следующее верно:
- рассматриваемая функция не используется вне одной области видимости
- присвоение экземпляру имени помогает удобочитаемости (в отличие, например, от того, что логика достаточно проста, чтобы сразу увидеть, что происходит)
- нет других причин программирования, почему одна форма была бы предпочтительнее другой.
Мое текущее мнение по этому вопросу состоит в том, что добавление частного метода и ссылка на него методом ссылки является лучшим подходом. Такое ощущение, что это то, как эта функция была разработана для использования, и кажется, что легче сообщить о происходящем через имена и сигнатуры методов (например, «boolean isResultInFuture (Result result)» явно говорит, что возвращает логическое значение). Это также делает приватный метод более пригодным для повторного использования, если будущее усовершенствование класса хочет использовать ту же проверку, но не нуждается в оболочке функционального интерфейса.
Мой коллега предпочитает иметь метод, который возвращает экземпляр интерфейса (например, «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());
}
}
против
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());
}
}
против
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
). Поэтому мой вопрос больше в том, лучше ли в данном конкретном случае, который я выкладываю, чем в том, существуют ли случаи, когда один лучше другого, или наоборот.