Базовое использование необязательного класса в Java 8

Java

В 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. Указывает, что вы столкнулись с вложенным необязательным механизмом.

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"));

}

резюме

  1. Нулевые ссылки исторически вводились в языки программирования для обозначения отсутствия значений переменных.
  2. В Java 8 появился новый класс java.util.Optional для моделирования наличия или отсутствия значений переменных.
  3. Вы можете создавать необязательные объекты, используя статические фабричные методыOptional.empty,Optional.of иOptional.ofNullable.
  4. Класс Optional поддерживает несколько методов, таких как map, flatMap, filter, которые концептуально очень похожи на соответствующие методы класса Stream.
  5. Использование Optional заставит вас более агрессивно разыменовывать необязательные объекты для обработки отсутствующих значений переменных, и, в конечном счете, вы будете более эффективно предотвращать неожиданные исключения нулевого указателя в своем коде.
  6. Использование Необязательного может помочь вам разработать лучшие API.Пользователям нужно только прочитать сигнатуру метода, чтобы узнать, принимает ли метод Необязательное значение.