Используется для факультативного


используя Java 8 теперь в течение 6 + месяцев или около того, я очень доволен новыми изменениями API. Одна область, в которой я все еще не уверен, - это когда использовать Optional. Я, кажется, качаться между желанием использовать его везде что-то может быть null, и нигде вообще.

кажется, есть много ситуаций, когда я мог бы использовать его, и я никогда не уверен, добавляет ли он преимущества (читаемость / нулевая безопасность) или просто вызывает дополнительные накладные расходы.

Итак, у меня есть несколько примеров, и Мне было бы интересно узнать мнение сообщества о том,Optional выгодно.

1 - как открытый тип возврата метода, когда метод может вернуть null:

public Optional<Foo> findFoo(String id);

2 - как параметр метода, когда параметр может быть null:

public Foo doSomething(String id, Optional<Bar> barOptional);

3-как необязательный член Боба:

public class Book {

  private List<Pages> pages;
  private Optional<Index> index;

}

4-In Collections:

вообще я не думаю:

List<Optional<Foo>>

добавляет что-нибудь-тем более, что можно использовать filter() удалить null значения и т. д., Но есть ли хорошее применение для Optional в коллекции?

какие-нибудь дела я пропустил?

12 182

12 ответов:

главное Optional должен обеспечить средство для функции, возвращающей значение, чтобы указать на отсутствие возвращаемого значения. Смотрите эта дискуссия. Это позволяет вызывающему продолжить цепочку вызовов метода fluent.

это наиболее точно соответствует прецеденту #1 в вопросе ОП. Хотя,отсутствие значения - это более точная формулировка, чем null С чем-то вроде IntStream.findFirst не смог вернуться ноль.

для пользы дела #2, передавая необязательный аргумент методу, это может быть сделано для работы, но это довольно неуклюже. Предположим, у вас есть метод, который принимает строку, за которой следует необязательная вторая строка. Принимая Optional в качестве второго аргумента приведет в такой код:

foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

даже принятие null лучше:

foo("bar", "baz");
foo("bar", null);

наверное, лучше всего иметь перегруженный метод, который принимает один строковый аргумент и предоставляет значение по умолчанию для второго:

foo("bar", "baz");
foo("bar");

это имеет ограничения, но это гораздо лучше, чем любой из вышеперечисленных.

варианты использования #3 и #4, имеющего Optional в поле класса или в структуре данных считается неправильным использованием API. Во-первых, это идет вразрез с основной целью дизайна Optional как указано в верхней части. Во-вторых, это не добавляет никакой ценности.

есть три способа справиться с отсутствием значения в Ан Optional: чтобы предоставить замещающее значение, вызвать функцию для предоставления замещающего значения или создать исключение. Если вы сохраняете в поле, вы бы сделали это во время инициализации или назначения. Если вы добавляете значения в список, как упоминалось в OP, у вас есть дополнительный выбор просто не добавлять значение, тем самым "выравнивая" отсутствующие значения.

Я уверен, что кто-то может придумать некоторые надуманные случаи, когда они действительно хотят хранить Optional в поле или коллекция,но в целом, лучше избегать этого.

Я опаздываю на игру, но для чего это стоит, я хочу добавить свои 2 цента. Они идут против целью Optional, который содержится в ответ Стюарта Маркса, но я все еще убежден в их справедливости (очевидно).

Использовать Необязательно Везде

В Общем

Я написал целую сообщение в блоге об использовании Optional но это в основном сводится к это:

  • создайте свои классы, чтобы избежать опциональности везде, где это возможно
  • во всех остальных случаях, по умолчанию следует использовать Optional вместо null
  • возможно, сделать исключение для:
    • локальные переменные
    • возвращает значения и аргументы в частные методы
    • критические блоки кода производительности (без догадок, используйте профилировщик)

первые два исключения могут уменьшить воспринимаемые накладные расходы на перенос и разворачивание ссылок в Optional. Они выбираются таким образом, что null никогда не может юридически передать границу из одного экземпляра в другой.

обратите внимание, что это почти никогда не позволяют Optionals в коллекции, которые почти так же плохо, как nulls. просто не делайте этого. ;)

что касается ваших вопросов

  1. да.
  2. если перегрузка не является опцией, да.
  3. если другие подходы (подклассы, декорирование,...) нет выбора, да.
  4. пожалуйста, нет!

преимущества

это уменьшает присутствие nullв коде, хотя это не искореняет их. Но это даже не главное. Есть и другие важные преимущества:

Проясняет Намерения

используя Optional ясно выражает, что переменная, ну, необязательно. Любой читатель Вашего кода или потребитель вашего API будет бить по голове с тем, что там может быть ничего нет и что проверка необходима перед доступом к значению.

Удаляет Неопределенность

без Optional значение a null появление непонятно. Это может быть юридическое представительство государства (см. Map.get) или ошибка реализации, например, отсутствующая или неудачная инициализация.

это резко меняется при постоянном использовании Optional. Здесь, уже появление null означает наличие ошибки. (Потому что если значение было разрешено отсутствовать, то Optional был бы использован.) Это делает отладку исключения нулевого указателя намного проще, так как вопрос о значении этого null уже ответил.

Дополнительные Проверки

теперь, когда ничего не может быть null больше, это может быть применено везде. Будь то с аннотациями, утверждениями или простыми проверками, вам никогда не придется думать о является ли этот аргумент или что тип возвращаемого значения могут быть null. Не может быть!

недостатки

конечно, нет серебряной пули...

производительность

перенос значений (особенно примитивов) в дополнительный экземпляр может привести к снижению производительности. В узких петлях это может стать заметным или даже хуже.

обратите внимание, что компилятор может быть в состоянии обойти дополнительную ссылку для недолгой жизни Optionals. в Java 10 типы значений может дополнительно уменьшить или удалить штраф.

сериализация

Optional не является сериализуемым но a решение не слишком сложная.

инвариантность

из-за инвариантности универсальных типов в Java некоторые операции становятся громоздкими, когда фактический тип значения помещается в аргумент универсального типа. Приведен пример здесь (см. "параметрический полиморфизм").

лично я предпочитаю использовать инструмент проверки кода IntelliJ использовать @NotNull и @Nullable проверяет, как это в основном компиляции (может иметь некоторые проверки во время выполнения) это имеет более низкие накладные расходы с точки зрения читаемости кода и производительности. Это не так строго, как использование опционально, однако это отсутствие строгости должно быть подкреплено достойными модульными тестами.

public @Nullable Foo findFoo(@NotNull String id);

public @NotNull Foo doSomething(@NotNull String id, @Nullable Bar barOptional);

public class Book {

  private List<Pages> pages;
  private @Nullable Index index;

}

List<@Nullable Foo> list = ..

это работает с Java 5 и нет необходимости обернуть и развернуть значения. (или создать обертку объекты)

я думаю, что гуава необязательно и их вики-страница ставит его довольно хорошо:

помимо увеличения читаемости, которое происходит от предоставления null имени, самым большим преимуществом Optional является его идиотизм. Это заставляет вас активно думать об отсутствующем случае, если вы хотите, чтобы ваша программа компилировалась вообще, так как вам нужно активно развернуть необязательный и адресовать этот случай. Null делает его тревожно легко просто забыть вещи, и хотя FindBugs помогает, мы не думаю, что это решает проблему почти так же.

это особенно актуально, когда вы возвращаете значения, которые могут или не могут присутствовать"."Вы (и другие) гораздо чаще забываете об этом другом.метод (a, b) может возвращать нулевое значение, чем вы, вероятно, забудете, что a может быть null при реализации другого.метод. Возврат необязательно делает невозможным для вызывающих абонентов забыть этот случай, так как они должны сами развернуть объект для своего кода, чтобы компилировать. -- (Источник: Guava Wiki-использование и избегание null-в чем смысл?)

Optional добавляет некоторые накладные расходы, но я думаю, что его явным преимуществом является сделать это явно что объект может отсутствовать, и это заставляет программистов обрабатывать ситуацию. Это мешает тому, что кто-то забывает любимого != null проверка.

пример 2, Я думаю, что это гораздо более явный код напишите:

if(soundcard.isPresent()){
  System.out.println(soundcard.get());
}

чем

if(soundcard != null){
  System.out.println(soundcard);
}

для меня Optional лучше отражает тот факт, что нет звуковой карты нету.

мои 2¢ о ваших точках:

  1. public Optional<Foo> findFoo(String id); - Я не уверен в этом. Может быть, я вернусь Result<Foo> что может быть пустой или содержать Foo. Это аналогичная концепция, но не совсем Optional.
  2. public Foo doSomething(String id, Optional<Bar> barOptional); - Я бы предпочел @Nullable и проверку findbugs, как в ответ Питера Лоури см. Также эта дискуссия.
  3. ваша книга пример - я не уверен, если бы я использовал дополнительный внутренне, что может зависеть от сложности. Для " API " книги я бы использовал Optional<Index> getIndex() чтобы явно указать, что книга может не иметь индекс.
  4. я бы не использовал его в коллекциях, а не разрешал нулевые значения в коллекциях

В общем, я бы постарался минимизировать прохождение вокруг nulls. (однажды сожгли...) Я думаю, что стоит найти соответствующие абстракции и указать коллегам-программистам, что на самом деле представляет собой определенное возвращаемое значение.

с Oracle tutorial:

цель Optional - не заменить каждую пустую ссылку в вашей кодовой базе, а скорее помочь разработать лучшие API, в которых-просто читая подпись метода-пользователи могут сказать, следует ли ожидать необязательное значение. Кроме того, опция заставляет вас активно разворачивать необязательный элемент, чтобы справиться с отсутствием значения; в результате вы защищаете свой код от непреднамеренного нулевого указателя исключения.

вот интересное использование (Я считаю) для... Тесты.

Я намерен тщательно протестировать один из моих проектов, и поэтому я строю утверждения; только есть вещи, которые я должен проверить, а другие-нет.

public final class NodeDescriptor<V>
{
    private final Optional<String> label;
    private final List<NodeDescriptor<V>> children;

    private NodeDescriptor(final Builder<V> builder)
    {
        label = Optional.fromNullable(builder.label);
        final ImmutableList.Builder<NodeDescriptor<V>> listBuilder
            = ImmutableList.builder();
        for (final Builder<V> element: builder.children)
            listBuilder.add(element.build());
        children = listBuilder.build();
    }

    public static <E> Builder<E> newBuilder()
    {
        return new Builder<E>();
    }

    public void verify(@Nonnull final Node<V> node)
    {
        final NodeAssert<V> nodeAssert = new NodeAssert<V>(node);
        nodeAssert.hasLabel(label);
    }

    public static final class Builder<V>
    {
        private String label;
        private final List<Builder<V>> children = Lists.newArrayList();

        private Builder()
        {
        }

        public Builder<V> withLabel(@Nonnull final String label)
        {
            this.label = Preconditions.checkNotNull(label);
            return this;
        }

        public Builder<V> withChildNode(@Nonnull final Builder<V> child)
        {
            Preconditions.checkNotNull(child);
            children.add(child);
            return this;
        }

        public NodeDescriptor<V> build()
        {
            return new NodeDescriptor<V>(this);
        }
    }
}

в классе NodeAssert я делаю следующее:

public final class NodeAssert<V>
    extends AbstractAssert<NodeAssert<V>, Node<V>>
{
    NodeAssert(final Node<V> actual)
    {
        super(Preconditions.checkNotNull(actual), NodeAssert.class);
    }

    private NodeAssert<V> hasLabel(final String label)
    {
        final String thisLabel = actual.getLabel();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is null! I didn't expect it to be"
        ).isNotNull();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is not what was expected!\n"
            + "Expected: '%s'\nActual  : '%s'\n", label, thisLabel
        ).isEqualTo(label);
        return this;
    }

    NodeAssert<V> hasLabel(@Nonnull final Optional<String> label)
    {
        return label.isPresent() ? hasLabel(label.get()) : this;
    }
}

что означает, что утверждение действительно срабатывает только в том случае, если я хочу проверить метку!

кажется Optional полезно только в том случае, если тип T в необязательном является примитивным типом, например int,long,char и т. д. Для "реальных" классов, это не имеет смысла для меня, как вы можете использовать null значение в любом случае.

Я думаю, что это было взято отсюда (или из другой аналогичной концепции языка).

Nullable<T>

в C# это Nullable<T> был введен давно, чтобы обернуть типы значений.

Я не думаю, что Optional является общей заменой методов, которые потенциально возвращают нулевые значения.

основная идея заключается в следующем: отсутствие значения не означает, что оно потенциально доступно в будущем. Это разница между findById(-1) и findById (67).

основная информация Optionals для вызывающего абонента заключается в том, что он может не рассчитывать на заданное значение, но оно может быть доступно в какое-то время. Может быть, он снова исчезнет и вернется позже еще один время. Это как выключатель вкл / выкл. У вас есть "опция", чтобы включить или выключить свет. Но у вас нет выбора, если у вас нет света, чтобы включить.

поэтому я нахожу это слишком грязным, чтобы вводить опциональные везде, где ранее null был потенциально возвращен. Я по-прежнему буду использовать null, но только в ограниченных областях, таких как корень дерева, ленивая инициализация и явные методы поиска.

1 - как открытый тип возврата метода, когда метод может возвращать null:

здесь хорошая статья что показывает полезность usecase #1. Там этот код

...
if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            if (isocode != null) {
                isocode = isocode.toUpperCase();
            }
        }
    }
}
...

преобразуется в это

String result = Optional.ofNullable(user)
  .flatMap(User::getAddress)
  .flatMap(Address::getCountry)
  .map(Country::getIsocode)
  .orElse("default");

С помощью необязательного в качестве возвращаемого значения соответствующего геттер методы.

Java SE 8 представляет новый класс под названием java.утиль.Необязательно,

вы можете создать пустой необязательный или факультативный со значением null.

Optional<String> emptyOptional= Optional.empty(); 

и вот необязательное значение с ненулевым значением:

String valueString = new String("TEST");
Optional<String > optinalValueString = Optional.of(valueString );

сделайте что-нибудь, если значение присутствует

теперь, когда у вас есть необязательный объект, вы можете получить доступ к методам, доступным для явной обработки наличия или отсутствия значений. Вместо того, чтобы помнить, чтобы сделать null проверьте, как следует:

String nullString = null;
if(nullString != null){
  System.out.println(nullString );
}

вы можете использовать метод ifPresent () следующим образом:

Optional<String> optinalString= null;
optinalString.ifPresent(System.out::println);



package optinalTest;

import java.util.Optional;

public class OptionalTest {

    public Optional<String> getOptionalNullString() {
        return null;
//      return Optional.of("TESt");
    }

    public static void main(String[] args) {

        OptionalTest optionalTest = new OptionalTest();

        Optional<Optional<String>> optionalNullString =    Optional.ofNullable(optionalTest.getOptionalNullString());
        if (optionalNullString.isPresent()) {

            System.out.println(optionalNullString.get());

        }

    }
}

Если вы не хотите, чтобы проверить этот путь

if(obj!=null){

}

тогда вы можете сделать это таким образом, это все, что я вижу

if(Optional.ofNullable(obj).isPresent()){

}

в основном, это дает иллюзию, что вы более эффективно обрабатываете нулевые значения.