Как вернуть, когда опция пуста?

Мне нравится, что опционы теперь находятся в стандартной библиотеке Java. Но есть одна основная проблема, с которой я постоянно сталкиваюсь, в которой я не понял, как решить в лучшем (проще всего читать и понимать, красивейший, самый короткий) способ:

Как вернуться из метода, когда опция пуста?

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

В следующих примерах я попытаюсь показать, что я имею в виду:

void m1() {
    // When I get an optional:
    Optional<String> o = getOptional();

    // And want to return if it empty
    if (!o.isPresent()) return;

    // In the whole rest of the method I have to call Optional.get 
    // every time I want the value:
    System.out.println(o.get());

    // Which is pretty ugly and verbose!
}


void m2() {
    // If I instead return null if a value is absent:
    String s = getNullabe();
    if (s == null) return;

    // Then I can use the value directly:
    System.out.println(s);
}

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

Остальные примеры иллюстрируют это больше.

void m3() {
    // If I on the other hand want to throw on empty that pretty and compact:
    String s = getOptional()
        .orElseThrow(IllegalStateException::new);

    System.out.println(s);
}

void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;

    // I can of course declare a new variable for the un-optionalised string:
    String s = o.get();

    System.out.println(s);

    // But the old variable still remains in scope for the whole method 
    // which is ugly and annoying.
    System.out.println(o.get());
}


void m5() {
    // This is compact and maybe pretty in some ways:
    getOptional().ifPresent(s -> {
        System.out.println(s);

        // But the extra level of nesting is annoying and it feels 
        // wrong to write all the code in a big lambda.

        getOtherOptional().ifPresent(i -> {
            // Also, more optional values makes it really weird and 
            // pretty hard to read,  while with nullables I would 
            // get no extra nesting, it would looks good and be 
            // easy to read.
            System.out.println("i: " + i);

            // It doesn't work in all cases either way.
        });
    });
}


Optional<String> getOptional() {
    throw new UnsupportedOperationException();
}

Optional<Integer> getOtherOptional() {
    throw new UnsupportedOperationException();
}

String getNullabe() {
    throw new UnsupportedOperationException();
}

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

Или, если это невозможно, чтобы получить все это, каков наилучший способ справиться с этой ситуацией?

Ответ 1

Вы можете использовать orElse(null):

String o = getOptional().orElse(null);
if (o == null) {
    return;
}

Ответ 2

ifPresent вы можете использовать ifPresent и map, если функция недействительна, и вам нужно делать побочные эффекты, которые вы можете использовать ifPresent,

optional.ifPresent(System.out::println); 

Если другой метод возвращает полагается на Необязательный, то этот метод может также потребовать вернуть Необязательный, а также использовать метод карты

Optional<Integer> getLength(){
    Optional<String> hi = Optional.of("hi");
    return hi.map(String::length)
}

Большую часть времени, когда вы вызываете isPresent и get, вы злоупотребляете Optional.

Ответ 3

Использование ifPresent, которое вы используете, не требует создания новой лямбды, вы можете просто использовать ссылку на метод:

getOptional().ifPresent(System.out::println);

Это на самом деле не решает проблему, когда вы хотите условно определить наличие двух опций. Но в качестве альтернативы

// And want to return if it empty
if (!o.isPresent()) return;

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

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

Однако такой вариант использования предполагает, что вы действительно не пользуетесь опцией, а не нулевым значением. В общем случае, если вы используете isPresent и get, то необязательно, что вы действительно не получите столько всего (кроме того, что это заставляет вас рассмотреть случай, когда значение отсутствует). Использование ifPresent, map, filter и других "более функциональных" методов может быть более типичным для необязательного значения.


Но в любом случае, пожалуйста, не возвращайте значение null, когда вы обещаете Необязательный. Хотя совершенно законно возвращать значение null, когда объект ожидает, точкой необязательного является то, чтобы избежать необходимости проверять значение null. Так что не делайте:

Optional<String> getOptional() {
    return null;
}

но вместо этого:

Optional<String> getOptional() { 
  return Optional.empty();
}

В противном случае вам придется делать:

Optional<String> o = getOptional();
if (o != null && o.isPresent()) {
  // ...
}

который на самом деле просто делает то же самое в два раза. Используйте опцию или используйте значение с нулевым значением, но не делайте того и другого!

Ответ 4

Я не думаю, что то, что вы просите, на самом деле возможно, но я хотел бы предложить просто взять весь ваш код, который работает непосредственно на вашей String и обернуть его в функцию. Таким образом, ваша функция становится примерно такой:

void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;

    doThings(o.get());
}

void doThings(String s){
    System.out.println(s);
    //do whatever else with the string.
}

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

Ответ 5

Это отличная тема, мы все любим функциональный стиль программирования! :)

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

ШАГ 1

Тот факт, что вы перестали думать, является первым шагом и подтверждением того, что вы на правильном пути!

ШАГ 2 - Исследуйте

public void processMedia(String mediaClassName, String mediaName) {

    // THAT MIGHT BE YOUR FIRST IDEA
    MediaClass mediaClass = mediaClassFinder.find(mediaClassName).orElse(null); 

    // RETURNING ON NULL CONDITION LIKE THE BELOW CAN BE ALRIGHT,
    // BUT POSSIBLY YOU CAN DO BETTER
    if (mediaClass == null) {
        return;
    }
    Optional<Media> media = mediaFinder.find(mediaClass.getId(), mediaName);

    // do processing

    // render the processed object
}

ШАГ 3 Наилучшим подходом может быть выделение различных частей реализации в отдельные методы и их объединение в функциональном стиле. В качестве побочного эффекта этого упражнения вы, вероятно, получите значительно улучшенный интерфейс и структуру вашего приложения. Вот как работает рефакторинг. Посмотрите ниже, нет явных нулевых назначений и никаких дополнительных точек возврата нигде. И кодирование становится веселым.

public void processMedia(String mediaClassName, String mediaName) {
    mediaClassFinder.find(mediaClassName)
        .flatMap(mediaClass -> mediaFinder.find(mediaClass.getId(), mediaName))
        .map(this::compress)
        .ifPresent(this::render);
}
private Media compress(Media media) {
    // compress media implementation
    return media;
}
private void render(Media media) {
    // render media implementation
}

Я надеюсь, вам понравится мой пример :)