1

I have a class that can contain multiple optional IDs, the class will pick the first available and return it to the caller. Something like the below.

Optional<Phone> homePhone;
Optional<Phone> mobilePhone;
Optional<Phone> emergencyPhone;

Phone getBestPhone() {
  return homePhone.map(p -> p)
           .orElse(mobilePhone.map(p -> p)
             .orElse(emergencyPhone.map(p -> p)
               .orElseThrow(() -> new IllegalArgument("No valid phone")))));
}

I'd like to use the optional methods like map and orElse but in this case it leads to way too much nesting. Two other pseudocode options might be.

Phone getBestPhone() {
  homePhone.ifPresent(p -> { return p; });
  mobilePhone.ifPresent(p -> { return p; });
  emergencyPhone.ifPresent(p -> { return p; });
  throw new IllegalArgument("...");
}
Phone getBestPhone() {
  return FirstPresent(homePhone, mobilePhone, emergencyPhone);
}

Are there any better ways that the existing method I have? I'm very tempted to avoid the nesting by doing vanilla isPresent() checks.

user3875080
  • 63
  • 1
  • 5

1 Answers1

1

IMO, firstPersent approach seems more readable and extensible. We can make use of Stream#of(T...) method to wrap those Optionals to a stream and then find the first present.

import java.util.Optional;
import java.util.stream.Stream;

public class GetFirstPresent {
    public static void main(String[] args) {
        Optional<Phone> homePhone = Optional.empty();
        Optional<Phone> mobilePhone = Optional.empty();
        Optional<Phone> emergencyPhone = Optional.of(new Phone("e123"));
        Phone phone = firstPresent(homePhone, mobilePhone, emergencyPhone);
        System.out.println(phone.id);
        try {
            firstPresent(homePhone, mobilePhone);
        } catch (IllegalArgumentException e) {
            System.out.println(e);
        }
    }

    @SafeVarargs
    private static Phone firstPresent(Optional<Phone>... phones) {
        return Stream.of(phones).filter(Optional::isPresent)
                .findFirst().map(Optional::get).orElseThrow(() -> {
                    return new IllegalArgumentException("No phone present");
                });
    }

    private static class Phone {
        public Phone(String id) {
            super();
            this.id = id;
        }

        private String id;
    }
}

One draw back is we need @SafeVarargs to suppress warning, which cannot be avoided according to Is it possible to solve the “A generic array of T is created for a varargs parameter” compiler warning?

samabcde
  • 6,988
  • 2
  • 25
  • 41