В Java 8 появился очень интересный класс Optional, предназначенный главным образом для устранения печально известного исключения NullPointerException. Когда мы проверяем свойство объекта, чтобы увидеть, находится ли его значение в ожидаемом формате, только для того, чтобы узнать, что мы смотрим не на объект, а на нулевой указатель, который немедленно вызывает раздражающее NullPointerException.
бросать кирпичи
Давайте рассмотрим простой пример:
String address = world.getCountry.getCity.getName;
Перед получением адреса необходимо проверить каждый класс, чтобы предотвратить исключения нулевого указателя:
public String getAddress(World world){
if (world != null){
Country country = world.getCountry();
if (country!=null){
City city = country.getCity();
if (city != null){
return city.getName();
}
}
}
return "unknown";
}
Вы можете видеть, насколько сложны приведенные выше проверки, код полон нулевых проверок, а читабельность ужасна.
Начало работы с необязательным классом
Когда переменная существует, класс Optional просто инкапсулирует класс. Если переменная не существует, отсутствующее значение моделируется как «пустой» необязательный объект, возвращаемый методом Optional.empty() . Тогда вам может быть интересно, в чем разница между null и Optional.empty()? Семантически вы можете относиться к ним как к одному и тому же, но на практике они очень разные: если вы попытаетесь разыменовать нулевое значение, гарантируется исключение NullPointerException, но с Optional.empty() это совершенно нормально, что является допустимым объектом класса Необязательный класс, который можно вызывать в различных сценариях, и он очень полезен.
Несколько шаблонов для применения Необязательно
Создайте экземпляр необязательного объекта
Можно создать пустой необязательный экземпляр объекта
@Test(expected = NoSuchElementException.class)
public void createOptionalObject(){
Optional<String> country = Optional.empty();
country.get();
}
Нет сомнений, что когда мы вызываем метод get(), будет сообщено об исключении NoSuchElementException.
Вы также можете использовать методы of() и ofNullable() для создания экземпляра Optioanal, содержащего значение. Разница в том, что если вы передадите null в качестве параметра для of(), будет сообщено об исключении нулевого указателя, поэтому объект может существуют или нет.Вы должны использовать ofNullable()
@Test
public void createOptionalObject(){
Optional<String> country = Optional.of("中国");
Optional<String> city = Optional.ofNullable("上海");
Optional<String> world = Optional.ofNullable(null);
//下面会报空指针异常
Optional<String> province = Optional.of(null);
}
Как получить значение в необязательной переменной? Необязательный предоставляет метод get(). Однако метод get также будет генерировать исключение, когда встретит пустой необязательный объект, поэтому его нетрадиционное использование вернет нас в кошмар обслуживания кода, вызванный нулевым значением.
Доступ к значению необязательного объекта
Один из способов вернуть объект фактического значения из экземпляра Optional — использовать метод get():
@Test
public void getOptionalObject(){
String country = "China"
Optional<String> countryName = Optional.ofNullable(country);
Assert.assertEquals("China",countryName.get());
}
Конечно, этот метод вызовет исключение, если значение равно null.Чтобы избежать исключений, сначала проверьте
@Test
public void getOptionalObject(){
City city = new City("ShangHai");
Optional<City> sample = Optional.ofNullable(city);
Assert.assertTrue(sample.isPresent());
Assert.assertEquals(city.getName(),sample.get().getName());
}
Существует еще один метод ifPresent() для проверки наличия значения.Помимо проверки, этот метод также передает параметр Consumer (потребитель), если объект не пустой, он выполнит переданное в Lambda выражение
@Test
public void getOptionalObject(){
City city = new City("ShangHai");
Optional<City> sample = Optional.ofNullable(city);
sample.ifPresent(c -> Assert.assertEquals(city.getName(),sample.get().getName()));
}
Утвердить, если объект не является нулевым
вернуть значение по умолчанию
Необязательный предоставляет API для возврата значения объекта или для возврата значения по умолчанию, если объект пуст.
@Test
public void getOptionalObject(){
City city = null;
City city1 = new City("ShangHai");
City sample = Optional.ofNullable(city).orElse(city1);
Assert.assertEquals(city1.getName(),sample.getName());
}
Второй API того же типа — orElseGet(), который ведет себя немного иначе. Этот метод вернет значение при наличии значения, если значения нет, он выполнит функциональный интерфейс Поставщика, переданный в качестве параметра, и вернет результат своего выполнения:
City sample = Optional.ofNullable(city).orElseGet(() -> city1);
вернуть исключение
Необязательный также определяет API orElseThrow(), который выдает исключение, если объект пуст.
@Test(expected = IllegalArgumentException.class)
public void throwOptionalException(){
City city = null;
City sample = Optional.ofNullable(city).orElseThrow(() -> new IllegalArgumentException());
}
город пуст, поэтому будет выброшено исключение IllegalArgumentException
Этот метод дает нам более богатую семантику о том, какое исключение следует генерировать вместо того, чтобы всегда генерировать исключение NullPointerException.
Практический пример использования Необязательно
Извлечение и преобразование значений из необязательных объектов с помощью карты
Извлечение информации из объектов является относительно распространенным шаблоном, и для поддержки этого шаблона необязательный предоставляет метод карты. Это работает следующим образом:
@Test
public void getCityName(){
City city = new City("ShangHai");
Optional<City> sample = Optional.ofNullable(city);
Optional<String> name = sample.map(City::getName);
}
map применяет (вызывает) функцию в качестве параметра к значению, а затем оборачивает возвращаемое значение в необязательный элемент, что позволяет связать тестовые вызовы с возвращаемым значением.Можно ли провести рефакторинг предыдущего кода?
public Optional<String> getCityName(World world){
Optional<World> real = Optional.ofNullable(world);
Optional<String> name =
real.map(World::getCountry)
.map(Country::getCity)
.map(City::getName);
return name;
}
Однако этот код не может быть скомпилирован.. Нет проблем с тем, что real.map(World::getCountry) возвращает экземпляр Optional, но последующие вызовы map будут генерировать объекты типа Optional
Двухслойная необязательная структура объекта
Цепочка необязательных объектов с помощью flatMap
Итак, как нам решить эту проблему? Давайте вернемся к шаблону, который вы только что использовали для потоков: методу flatMap. При использовании потоков метод flatMap принимает в качестве аргумента функцию, а возвращаемое значение этой функции — другой поток. Этот метод применяется к каждому элементу в потоке, в результате чего создается новый поток потоков. Но flagMap заменяет каждый вновь созданный поток содержимым потока. Другими словами, потоки, созданные методом, объединяются или выравниваются в один поток. Результат, который вы хотите здесь, на самом деле похож, но вы хотите объединить два слоя опций в один.
public Optional<String> getCityName(World world){
Optional<World> real = Optional.ofNullable(world);
Optional<String> name =
real.flagMap(World::getCountry)
.flagMap(Country::getCity)
.map(City::getName);
return name;
}
Используйте фильтр, чтобы удалить определенные значения
Вам часто нужно вызвать метод для объекта, поэтому сначала вам нужно проверить, является ли объект NULL
public void filterCity(City city){
Optional<City> real = Optional.ofNullable(city);
real.filter(c -> c!=null && "ShangHai"
.equals(c.getName()))
.ifPresent(x -> System.out.println("ok"));
}
резюме
- Нулевые ссылки исторически вводились в языки программирования для обозначения отсутствия значений переменных.
- В Java 8 появился новый класс java.util.Optional для моделирования наличия или отсутствия значений переменных.
- Вы можете создавать необязательные объекты, используя статические фабричные методыOptional.empty,Optional.of иOptional.ofNullable.
- Класс Optional поддерживает несколько методов, таких как map, flatMap, filter, которые концептуально очень похожи на соответствующие методы класса Stream.
- Использование Optional заставит вас более агрессивно разыменовывать необязательные объекты для обработки отсутствующих значений переменных, и, в конечном счете, вы будете более эффективно предотвращать неожиданные исключения нулевого указателя в своем коде.
- Использование Необязательного может помочь вам разработать лучшие API.Пользователям нужно только прочитать сигнатуру метода, чтобы узнать, принимает ли метод Необязательное значение.