Java 8 added the concept of functional interfaces, as well as numerous new methods that are designed to take functional interfaces. Instances of these interfaces can be succinctly created using method reference expressions (e.g. SomeClass::someMethod
) and lambda expressions (e.g. (x, y) -> x + y
).
A colleague and I have differing opinions on when it is best to use one form or another (where "best" in this case really boils down to "most readable" and "most in line with standard practices", as they're basically otherwise equivalent). Specifically, this involves the case where the following are all true:
- the function in question isn't used outside a single scope
- giving the instance a name helps readability (as opposed to e.g. the logic being simple enough to see what's happening at a glance)
- there aren't other programming reasons why one form would be preferable to the other.
My current opinion on the matter is that adding a private method, and referring that by method reference, is the better approach. It feels like this is how the feature was designed to be used, and it seems easier to communicate what is going on via method names and signatures (e.g. boolean isResultInFuture(Result result)
clearly is saying it's returning a boolean). It also makes the private method more reusable if a future enhancement to the class wants to make use of the same check, but doesn't need the functional interface wrapper.
My colleague's preference is to have a method which returns the instance of the interface (e.g. Predicate<Result> resultInFuture()
). To me, this feels like it's not quite how the feature was intended to be used, feels slightly clunkier, and seems like it's harder to really communicate intent through naming.
To make this example concrete, here is the same code, written in the different styles:
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
});
}
}
Are there any official or semi-official documentation or comments around whether one approach is more preferred, more in-line with the language designers' intents, or more readable? Barring an official source, are there any clear reasons why one would be the better approach?
Object::toString
). So my question is more about whether it's better in this particular type of instance that I'm laying out here, than whether there exist instances where one is better than the other, or vice versa.