Hello all!
First post here haha, I’ll get right to it
I was recently asked as a challenge to represent a validation method as a lambda expression and the implication was that this was somehow more efficient/effective… I can’t see it does anyone here have experience doing this and why you would want to use lambda expression for validation methods?
Especially when the lambda expression invoking the validation method need to throw Exceptions when the method returns false (leading to having to use Runtime Exceptions)
See example below
Predicate<String> check1 = x -> {
if(!x.matches("blah")) {
throw new RuntimeException();
}
return true;
};
This looks terribly inefficient to me, why do it this way?
Thanks in advance for any suggestions!
Most likely, they meant that when you are passing a Predicate
as an argument to some other method, it can be more efficient to use a lambda expression rather than creating an anonymous class (or creating a whole method with a method reference).
For instance, a generic filter
method on some custom data container could take a Predicate
:
class CustomDataContainer<T> {
/** Returns a filtered copy of this CustomDataContainer. */
CustomDataContainer<T> filter(Predicate<T> predicate) { ... }
}
Supposing we have a CustomDataContainer<String>
called container
we could apply that method using (1) an anonymous class to define the Predicate
, (2) a method reference, or (3) a lambda. Here are those three for a trivial case of having a predicate that always returns true
:
// Using an anonymous class
var container2 = container.filter(new Predicate() {
@Override
public boolean test(String someString) {
return true;
}
});
// Using a method reference:
var container3 = container.filter(SomeClass::alwaysTrue);
// Using a lambda:
var container4 = container.filter(unused -> true);
Lambdas are much better than using the anonymous class (much less verbose), but if the lambda goes more than a few lines of code, I would advise creating a method and using a method reference (even if it will only be used once).
For more general wisdom on some Java best-practices I would suggest checking out Effective Java by Joshua Block which does offer some best practices for lambdas in Java.
PS: The syntax you are using makes me assume Java, but you did not specify in your question so apologies if that was an incorrect assumption.
1 Like
When I say “efficient” here, that is perhaps imprecise. I should say “more readable” since efficiency is something you would probably need to benchmark. More like efficient in terms of “the programmer’s time reading and reasoning about the code” rather than efficiency in terms of “how fast your program runs” (or something along those lines).
1 Like
You are correct! It is in Java, I should have specified
Thank you so much! This was exactly what I was looking for, but just to make sure I am applying it correctly, would you say this is more or less along the lines of how you would approach writing code for this kind of problem?
Logger logger = LogManager.getLogger(Validator.class);
private void confirmOrThrow(Candidate candidate,
String message,
Predicate<Candidate> predicate) throws Exception {
if(!predicate.test(candidate)) {
var e = new Exception(message);
logger.error(e.getMessage(), e);
throw e;
}
}
// Log all the details of the exceptions being thrown from the validate() method of this class.
// Make sure to let every exception propagate.
public void validate(Candidate candidate) throws Exception {
confirmOrThrow(candidate, "Validator.INVALID_CANDIDATE_NAME", c -> isValidCandidateName(c.getCandidateName()));
confirmOrThrow(candidate, "Validator.INVALID_CANDIDATE_ID", c -> isValidCandidateId(c.getCandidateId()));
confirmOrThrow(candidate, "Validator.INVALID_DEPARTMENT", c -> isValidDepartment(c.getDepartment()));
confirmOrThrow(candidate, "Validator.INVALID_EXAM_DATE", c -> isValidExamDate(c.getExamDate()));
confirmOrThrow(candidate, "Validator.INVALID_EXAM_MARKS", this::isValidExamMarks);
confirmOrThrow(candidate, "Validator.INVALID_RESULT", c -> isValidResult(c.getResult()));
I actually have that book as well ^^; thank you for the prompt that I should probably spend some time reviewing it haha, so much to learn, so little time!
Cheers
Don
And agreed! Readability/maintainability also falls under efficiency in my books haha, but as you said a different kind to computational efficiency
Yes, that looks about right, but there are some other questionable practices in there.
For example: catching and throwing the same exception:
logger.error(e.getMessage(), e);
throw e;
Usually, an exception that propagates all the way through the application gets logged anyway so you get duplicate logging. And if the caller of this method actually handled the exception, it is doubtful that they would still want the log spam.
Also throwing Exception
rather than some specific exception (eg. InvalidArgumentException
) is usually a bad idea:
var e = new Exception(message); // should use something like IllegalArgumentException
In general, Exception
is just too broad of an exception to be much use except at the top level in a server that should always stay running so you probably want to throw some kind of validation exception in this sort of thing.
That being said, the use of lambdas looks about right.
1 Like
Thanks! Agree with the vagueness of Exception, I was just using it off the cuff as a generic example, should have used the good ol FooBar
As for throwing the exception after logging it, this was as requested from the challenge, and the context was the method being used as a Utility class method for Business logic implementation (2/3 tier architecture) and I thought that it was a more basic way of showing how one would re-throw this exception for say configuring a response entity at the controller if you didn’t have a framework handling things for you. This is only my best guess haha, still learning! And to be honest the logging itself would probably be done using an AOP framework.
Thanks again for your help! Really appreciate it
Don
1 Like