Этот вопрос похож на Чтение нескольких переменных из объекта, заключенного в Option [] , но в контексте Java вместо Scala.
Предположим, у меня есть метод, который возвращает Optional<Address>
.Мне нужно извлечь несколько полей из упакованного адреса, скажем, getCity()
, getCountry()
и getZIP()
.Если адрес отсутствует, следует использовать некоторые значения по умолчанию.Затем следует продолжить логику с результирующими значениями.
Вопрос в том, что является лучшим и наиболее идиоматическим способом сделать это.Я могу вспомнить следующие три:
1) if-else
City city;
Country country;
ZIP zip;
Optional<Address> optAddr = getAddress();
if (optAddr.isPresent()) {
Address addr = optAddr.get();
city = addr.getCity();
country = addr.getCountry();
zip = addr.getZIP();
}
else {
city = null;
country = Country.getUSA();
zip = ZIP.DEFAULT;
}
// ... use city, country, and zip
Плюсы:
- Не создано никаких дополнительных объектов - наиболее эффективный вариант.
- Случаи по умолчанию сгруппированы вместе.
Минусы:
2) Несколько цепочек
Optional<Address> optAddr = getAddress();
City city = optAddr.map(Address::getCity()
.orElse(null);
Country country = optAddr.map(Address::getCountry)
.orElseGet(Country::getUSA);
ZIP zip = optAddr.map(Address::getZIP)
.orElse(ZIP.DEFAULT);
// ... use city, country, and zip
Обратите внимание, что здесь логика немного отличается, поскольку значение по умолчанию применяется не только при отсутствии адреса, нотакже когда соответствующее поле адреса равно null
.
Но в моем случае это не проблема.
Плюсы:
- Краткий.
- Переменные немедленно инициализируются значением.
- Ближе к тому, как это делается с одним полем.
Минусы:
- Значения по умолчанию разбиты на части.
- Цепные вызовы
map
потенциально могут создавать промежуточные объекты (но идиоматическим образом). - Возможно, однотипное использование нескольких цепочеконет
Optional
?
3a) Перенос значений по умолчанию в объекте
private Address createDefaultAddress() {
Address addr = new Address();
addr.setCity(null);
addr.setCountry(Country.getUSA());
addr.setZIP(ZIP.DEFAULT);
return addr;
}
Address addr = getAddress().orElseGet(this::createDefaultAddress);
City city = addr.getCity();
Country country = addr.getCountry();
ZIP zip = addr.getZIP();
// ... use city, country, and zip
Плюсы:
Минусы:
- Поля упакованы в
Address
только для извлечения их сразу после этого.
3b) Обернуть значения по умолчанию в константу
Как предложено @HariMenon и @flakes, an Address
со значениями по умолчанию можно сохранить в константеи повторно используется для каждого вызова, чтобы уменьшить накладные расходы.
Можно также инициализировать эту «константу» лениво:
private Address defaultAddress;
private Address getDefaultAddress() {
if (defaultAddress == null) {
defaultAddress = new Address();
defaultAddress.setCity(null);
defaultAddress.setCountry(Country.getUSA());
defaultAddress.setZIP(ZIP.DEFAULT);
}
return defaultAddress;
}
Address addr = getAddress().orElseGet(this::getDefaultAddress);
// ... same as 3.1
Бонусный вопрос:
Предположим, у меня есть полный контроль над методом getAddress()
, и я знаю, что все случаи использования будут аналогичны этому примеру, но значения по умолчанию различаются.Должен ли я изменить его, чтобы он возвращал null
вместо Optional
, чтобы лучше приспособить решение if-else
?