3

There's a method called validate which as input accepts an instance of Option and a Predicate - two arguments (yeah, I know Option should be passed as an argument but this is simplified real-world scenario here. Now, if Option is empty I need to throw NotFoundException. When it has a value that doesn't match the Predicate passed it should fail with ForbiddenException. If it has value and it matches the predicate nothing happens.

So this will be:

                Option, Predicate
                     /\
          isEmpty() /  \ isDefined()  
                   /    \
            throw NFE    matches(Predicate)
                        / \
                    no /   \ yes 
                      /     \
                throw FE     end

I have some solutions but would like potential answerers to come to this question with clear mind ;) My problem is getting the first exception if both tests fail. I expect an elegant solution if I may expect anything ;) All vavr beings allowed (Either, Validation, Try..)

One of the ideas is to use double Option:

Option
  .of(o.getOrElseThrow(NotFoundException::new))
  .filter(p)
  .getOrElseThrow(ForbiddenException::new);

But this seems a bit awkward.

Here sample project may be found.

Opal
  • 81,889
  • 28
  • 189
  • 210

2 Answers2

5

As I see it, the best option would be to create readable code, which in this case means using two if statements:

if (!o.isPresent()) throw new NotFoundException();
if (!p.test(o.get())) throw new ForbiddenException();

Simple, readable and straightforward for whoever will need to maintain this code in the future.

If nonetheless you want to do it in some functionalish style:

o.orElseThrow(NotFoundException::new);
o.filter(p).orElseThrow(ForbiddenException::new);

With Java 9:

o.ifPresentOrElse(
    t -> if (!p.test(t)) throw new ForbiddenException(), 
    () -> throw new NotFoundException());
fps
  • 33,623
  • 8
  • 55
  • 110
  • 1
    Thanks! I'm aware of necessity of having readable code in the project. – Opal Nov 17 '17 at 07:24
  • 2
    I like both solutions for their simple and flat code. This is a good example for the benifits of Optionals not "expiring" after a get operation (get, orElse, orElseThrow, ifPresent...), and can be accessed multiple times. – Malte Hartwig Nov 17 '17 at 08:58
3

This would be the solution with Optional (don't have the time to check vavr but should be close enough)

public static <T> void validate( Optional<T> optional, Predicate<T> pred ) throws NotFoundException, ForbiddenException {
    if ( !pred.test( optional.orElseThrow( NotFoundException::new ) ) )
        throw new ForbiddenException();
}

Don't know a prettier way to throw that ForbiddenException unfortunately.

If you want a full functional solution, you will need an Optional

Optional.of( 
    pred.test( 
       option.orElseThrow( NotFoundException::new ) 
    ) 
).filter( b -> b ).orElseThrow( ForbiddenException::new );

The exception are thrown in the correct order since we only check the Predicate after the option check.

AxelH
  • 14,325
  • 2
  • 25
  • 55
  • Thanks, but using `if` statement that is easy ;) – Opal Nov 16 '17 at 21:13
  • @Opal That's the problem when you want to do everything in a "new" feature... you stop think the simple way ;) What is wrong using a condition on a `boolean`, no `NPE` possible on that predicate result (the point of `Optionnal` is mostly to stop have ` != null` checks). Keep it simple. – AxelH Nov 16 '17 at 21:16
  • I came to the same result (using `if`) independently. While it's not really functional, there was no reason to strive for a pure functional solution as the requirements themselves are asking for throwing exceptions. – Nándor Előd Fekete Nov 16 '17 at 21:29
  • @NándorElődFekete last solution, using an `Optional`, just to be able to `filter` seems overkill. I prefer the first one ;) – AxelH Nov 16 '17 at 21:32
  • Yea, I prefer the first one too. I don't consider using `if` an antipattern in functional style or something, especially since we're to throw exceptions. – Nándor Előd Fekete Nov 16 '17 at 21:34
  • @NándorElődFekete, you're absolutely right. Unfortunately this is legacy code with exceptions being thrown here and there, everywhere :/ – Opal Nov 17 '17 at 07:19