Трансляция содержимого необязательных или потоковых материалов

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

Для этой подзадачи в приложении я хочу, чтобы пользовательский объект экземпляра TreeNode был преобразован в CustomUserObject, при условии, что TreeNode является экземпляром DefaultMutableTreeNode.

private Optional<CustomUserObject> getCustomUserObject(TreeNode node) {
    Optional<DefaultMutableTreeNode> optDefaultMutableTreeNode = OptionalUtil.cast(Optional.ofNullable(node), DefaultMutableTreeNode.class);
    Optional<Object> optUserObject = optDefaultMutableTreeNode.map(DefaultMutableTreeNode::getUserObject); //
    return OptionalUtil.cast(optUserObject, CustomUserObject.class);
}


/**
 * Maps the given optional, if the containing element is an instance of the given class.
 * Returns empty if the containing object is not an instance of the given class.
 * 
 * @param orgOptional
 *        given optional
 * @param clazz
 *        given class.
 * @return the resulting {@link Optional}.
 */
public static <T, X> Optional<T> cast(Optional<X> orgOptional, Class<T> clazz) {
    return orgOptional //
        .filter(clazz::isInstance) // check instance
        .map(clazz::cast); // cast
}

/**
 * Maps the given stream, if the containing element is an instance of the given class.
 * Returns empty if the containing object is not an instance of the given class.
 * 
 * @param orgStream
 *        given optional
 * @param clazz
 *        given class.
 * @return the resulting {@link Optional}.
 */
public static <T, X> Stream<T> cast(Stream<X> orgStream, Class<T> clazz) {
    return orgStream //
        .filter(clazz::isInstance) // check instance
        .map(clazz::cast); // cast
}

Я помню, что мне довольно часто нужно приводить таким образом опции или потоки. Это не бегло. На самом деле я хочу, чтобы у java Optional или Stream был метод приведения, который выполняет вышеуказанный шаг. Я не хочу писать свой собственный CustomOptional. Я что-нибудь пропустил? Есть ли способ сделать это проще?

В сторону: добавьте ? extends ко всем переменным типа в параметрах метода, чтобы сделать API более гибким.

Andy Turner 31.10.2018 08:38

Напишите ? extends what где? Если я вас правильно понял, это не решит мою проблему, потому что мне нужно быть уверенным, что это DefaultMutableTreeNode или CustomUserObject, а не просто заявить об этом и пожелать, чтобы это было так.

michaeak 31.10.2018 08:45

В любом случае спасибо за подсказку - просто: в этом примере X и T не имеют отношения, поэтому extends не добавляет здесь никакой ценности.

michaeak 31.10.2018 08:55

но это так. Дело в том, что, используя Class<? extends T>, вы можете получить Optional<Object>, производный от Optional<Serializable>, который вы разливаете с помощью String.class. Это своего рода гибкость, которая ничего не стоит, но действительно раздражает, если у вас ее нет, в редких случаях, когда она вам нужна.

Andy Turner 31.10.2018 09:22

Другой случай, который может быть более вероятным, - это Optional<YourClass> opt = cast(otherOpt, instanceOfYourClass.getClass());. В нынешнем виде opt должен быть типа Optional<? extends YourClass>.

Andy Turner 31.10.2018 09:26

Прежде всего, я не вижу преимущества написания ? extends YourClass как результата метода, потому что все, что можно сделать с этим результатом, - это получить доступ к YourClass, методы или атрибуты подклассов не могут быть доступны с этим. Во-вторых, запись ? имеет некоторые проблемы и может привести к выводам SonarLint.

michaeak 31.10.2018 10:04

Основным преимуществом записи ? extends YourClass является то, что он компилируется, в то время как он не будет компилироваться только с YourClass.

Andy Turner 31.10.2018 10:06

Можете привести пример того, что вы хотите написать, а что не компилируется?

michaeak 31.10.2018 10:07

Конечно: ideone.com/HXaUE2. Попробуйте удалить ? extends из main.

Andy Turner 31.10.2018 10:08

Спасибо за ваши старания. Но для меня монета не падает. Если вы хотите применить приведение к классу, к которому хотите выполнить приведение. И писать ? для меня воняет.

michaeak 31.10.2018 10:14

Хорошо, но почему нужно выполнять приведение к динамическому классу, например instance.getClass()?

michaeak 31.10.2018 10:18

Хорошо, я думаю, что понял вашу точку зрения. Итак, вы говорите, что предпочитаете писать public static <T, X> Optional<T> cast(Optional<X> orgOptional, Class<? extends T> clazz) , чтобы он работал с динамическими классами, верно?

michaeak 31.10.2018 10:26
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
12
3 514
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете легко сделать это более плавным, полагаясь на методы map() / flatMap() и cast, которые вместо этого возвращают функции.

Для Optional это очень просто, поскольку map() может действовать как фильтр, возвращая null. Так что просто определите:

public static <U> Function<Object, U> filterAndCast(Class<? extends U> clazz) {
    return t -> clazz.isInstance(t) ? clazz.cast(t) : null;
}

и используйте его как:

Optional<Number> number = Optional.of(42L);
System.out.println(number.map(filterAndCast(Integer.class)));
System.out.println(number.map(filterAndCast(Long.class)));

Выход:

Optional.empty
Optional[42]

Для потоков вы можете применить более или менее тот же трюк, полагаясь на flatMap() с функцией, возвращающей пустой Stream:

public static <U> Function<Object, Stream<U>> streamFilterAndCast(Class<? extends U> clazz) {
    return t -> clazz.isInstance(t) ? Stream.of(clazz.cast(t)) : Stream.empty();
    // or alternatively
    return t -> Stream.of(t).filter(clazz::isInstance).map(clazz::cast);
}

и используйте его как:

Stream.of(42L, "Hello world", 1024, 3.14)
        .flatMap(streamFilterAndCast(Number.class))
        .forEach(System.out::println);

Выход:

42
1024
3.14

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

michaeak 01.11.2018 08:53

@michaeak Я понимаю часть "трудночитаемую" (хотя я думаю, что это чистое мнение), но что касается части, подверженной ошибкам, я думаю, что правда как раз наоборот. Использование if-else имеет гораздо больше возможностей для ошибки, чем троичное, потому что троичное более ограничено. Кроме того, операторы должны оперировать побочными эффектами, но тернарное выражение является выражением, что делает его более сложным. Поэтому, когда вы столкнетесь с тем, кто написал это руководство по стилю, скажите им, что я сказал, что все в порядке :)

Brian Goetz 19.03.2020 15:55

@ brian-goetz: у меня был проект, в котором один человек использовал 3 или 4 составных трояна. А также он широко используется для нулевых проверок, которые, как предполагается, должны выполняться Optional, поэтому для меня тернарный код не подходит, я не использую его сам, хотя я знаю, как он работает.

michaeak 23.03.2020 10:14

@michaeak Я уверен, что это раздражало, но вы приводите абсурдный аргумент, по сути: «поскольку кто-то однажды злоупотребил функцией, никто никогда не сможет использовать ее снова, даже если это абсолютно правильно». (Это все равно, что сказать «машины - это не хорошо», потому что кого-то однажды сбил пьяный водитель.) Тернар - важный инструмент в языковом наборе инструментов, и во многих случаях, когда он применяется, объективно лучший выбор, чем если- тогда, и при правильном использовании, он будет полностью читаемым. Вините плохих разработчиков в их плохом коде, а не в инструментах, которые они выбирают.

Brian Goetz 23.03.2020 15:24

Это я придумал для По желанию

/**
 * Creates a function that tries to cast to the given class.
 * If the given argument is not an instance of the given class <code>Optional.empty()</code> is returned.
 * This function can be used in an <code>optionalObject.flatMap(Functional.cast(SomeClass.class))</code> context.
 * 
 * @param clazz
 *        given Clazz
 * @return Returns an optional of the given class.
 * @param <X>
 *        generic input argument for the function
 * @param <T>
 *        type of the output of the function
 */
public static <X, T> Function<X, Optional<T>> cast(Class<? extends T> clazz) {
    return x -> {
        if (clazz.isInstance(x)) {
            return Optional.of(clazz.cast(x));
        }
        return Optional.empty();
    };
}

и используйте это так:

Optional<DefaultMutableTreeNode> optParent = Optional.of(node) // optional of original node
                    .map(ProfileTreeNode::getParent) // parent of this node.
                    .flatMap(FunctionalUtil.cast(DefaultMutableTreeNode.class)); // cast to DefaultMutableTreeNode
                optParent.ifPresent(y -> y.remove(node));

Для потоки написал

/**
 * Creates a function that tries to cast to the given class.
 * If the given argument is not an instance of the given class an empty stream is returned.
 * This function can be used in an <code>stream.flatMap(Functional.castStream(SomeClass.class))</code> context.
 * 
 * @param clazz
 *        given Clazz
 * @return Returns an optional of the given class.
 * @param <X>
 *        generic input argument for the function
 * @param <T>
 *        type of the output of the function
 */
public static <X, T> Function<X, Stream<T>> castStream(Class<? extends T> clazz) {
    return x -> Stream.of(x) // stream of x.
        .filter(clazz::isInstance) // check if instance
        .map(clazz::cast);
}

Другие вопросы по теме