Как преобразовать необязательный параметр в OptionalInt?

у меня есть Optional что я хочу "преобразовать" к OptionalInt, но, похоже, нет простого способа сделать это.

вот что я хочу сделать (пример надуманный):

public OptionalInt getInt() {
    return Optional.ofNullable(someString).filter(s -> s.matches("d+")).mapToInt(Integer::parseInt);
}
нет mapToInt() метод Optional.

лучшее, что я мог придумать:

return Optional.ofNullable(someString)
    .filter(s -> s.matches("d+"))
    .map(s -> OptionalInt.of(Integer.parseInt(s)))
    .orElse(OptionalInt.empty());

но это кажется безвкусным.

я пропустил что-то из JDK, что может сделать преобразование более элегантным?

4 ответов


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

public OptionalInt getInt() {
    return Stream.of(someString).filter(s -> s != null && s.matches("\d+"))
        .mapToInt(Integer::parseInt).findAny();
}

С Java 9 вы можете использовать

public OptionalInt getInt() {
    return Stream.ofNullable(someString).filter(s -> s.matches("\d+"))
        .mapToInt(Integer::parseInt).findAny();
}

как уже было сказано, ни один из них не является более читаемым, чем обычное условное выражение, но я думаю, что он по-прежнему выглядит лучше, чем использование mapOrElseGet (и первый вариант не нуждается в Java 9.


нет, нет способа сделать это более элегантно, используя стандартный Java API. И насколько я знаю, добавлять такие методы в JDK-9 не планируется. Я спросил Пола Сандоса о добавлении mapToInt, etc., здесь's его ответ:

Я:

разве это не хорошая идея, чтобы обеспечить способ для передачи между Optional вроде mapToInt, mapToObj, etc., как это делается в Stream API?

пол:

я не хочу идти туда, мой ответ transform Optional* на *Stream. Аргумент для добавления mapOrElseGet (обратите внимание, что примитивные варианты возвращают U) является то, что другие функции могут быть составлены из него.

таким образом, вы, вероятно, будете иметь в Java-9:

return Optional.of(someString).filter(s -> s.matches("\d+"))
     .mapOrElseGet(s -> OptionalInt.of(Integer.parseInt(s)), OptionalInt::empty);

но не более того.

это потому, что авторы JDK настаивают на том, что Optional класс и его примитивные друзья (особенно примитивные друзья) не должны широко использоваться, это просто удобный способ выполнения ограниченного набора операций над возвращаемым значением методов, которые могут возвращать "отсутствие значения". Также примитивные optionals предназначены для повышения производительности, но на самом деле это гораздо менее значимо, чем с потоками, поэтому использование Optional<Integer> тоже хорошо. С проектом Valhalla (надеюсь, чтобы прибыть в Java-10) вы сможете использовать Optional<int> и OptionalInt станет ненужной.

в вашем конкретном случае лучший способ сделать это использование тернарного оператора:

return someString != null && someString.matches("\d+") ? 
       OptionalInt.of(Integer.parseInt(someString)) : OptionalInt.empty();

Я предполагаю, что вы хотите вернуть OptionalInt из метода. В противном случае еще более сомнительно, Зачем вам это нужно.


если у вас есть какой-либо объект, а не только String, вы можете временно перейти через Stream:

public static <T> OptionalInt toOptionalInt(Optional<T> optional, ToIntFunction<? super T> func) {
  return optional.map(Stream::of).orElseGet(Stream::empty)
    .mapToInt(func)
    .findFirst();
}

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

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

public static <T> OptionalInt toOptionalInt(Optional<T> optional, ToIntFunction<? super T> func) {
  if (optional.isPresent()) {
    return OptionalInt.of(func.applyAsInt(optional.get()));
  } else {
    return OptionalInt.empty();
  }
}

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

private static final Predicate<String> IS_DIGITS = Pattern.compile("^\d+$")
        .asPredicate();

public OptionalInt getInt() {
    return Optional.ofNullable(someString)
            .filter(IS_DIGITS)
            .map(Integer::valueOf)
            .map(OptionalInt::of)
            .orElseGet(OptionalInt::empty);
}

обратите внимание, что вам нужно привязать регулярное выражение, потому что asPredicate() использует find() вместо matches().

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

public OptionalInt getInt() {
    return Optional.ofNullable(someString)
            .map(Ints::tryParse)
            .map(OptionalInt::of)
            .orElseGet(OptionalInt::empty);
}