Utility Class для начальной загрузки лямбда-выражений или ссылок на методы для цепочки методов?

Благодаря функциональным интерфейсам, представленным в Java 8, вы можете легко связать различные выражения в одно новое выражение, как показано в фрагменте кода ниже.

public class PredicateChaining {
    public static void main(String[] args) {
        // verbose, but standard JDK functionality only
        Predicate<String> allUpperCase = StringUtils::isAllUpperCase;
        Predicate<String> longerThan5 = s -> s.length() > 5;
        if (allUpperCase.and(longerThan5).test("PREDICATE")) {
            System.out.println("'PREDICATE' is a uppercase text, longer than 5.");
        }

        // compact, but with utility method ('chain' in this case)
        if (chain(StringUtils::isAllLowerCase).and(s -> s.length() < 5).test("test")) {
            System.out.println("'test' is a lowercase text, shorter than 5.");
        }
    }

    public static <T> Predicate<T> chain(Predicate<T> test) {
        return test;
    }
}

В этом случае 2 String Predicate объединяются в один новый Predicate. Это прекрасно работает, когда у вас есть хотя бы один дескриптор (например, переменная) для вызова метода цепочки (в данном случае 'and'). Однако, если выражения, которые вы хотите связать, представлены в виде лямбда-выражения или ссылки на метод, вам нужно явно преобразовать одно из них в переменную, что делает его очень подробным. Когда компилятор не может определить точный тип, приходится прибегать к такой конструкции. Но в большинстве случаев это просто многословные накладные расходы, которых я хотел бы избежать.

В этом примере я сам написал служебный метод 'chain'. Но я могу создать один для других функциональных интерфейсов (Function, Consumer, Supplier, BiConsumer,... ) также.

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

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


person Erwin Dupont    schedule 19.02.2015    source источник
comment
На похожий вопрос уже был дан ответ, в котором говорилось, по сути, то же самое, что и @holger.   -  person Erwin Dupont    schedule 19.02.2015


Ответы (1)


Нет смысла создавать экземпляры Predicate, чтобы использовать их в последующем операторе if.

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

Например. при передаче Predicate в поток вы можете просто сделать два или более вызова filter:

stringStream.filter(StringUtils::isAllUpperCase).filter(s->s.length()>5). …

(и вы можете аналогичным образом объединить Function, используя несколько вызовов map)

и другие API могут иметь аналогичные методы.

Но в конце концов, если у вас есть два условия, которые вы хотите объединить, и ни одно из них уже не является экземпляром Predicate, прямым решением будет просто объединить выражения:

stringStream.filter(s -> isAllUpperCase(s) && s.length()>5). …

или как отдельное творение:

Predicate<String> allUpperCaseAndLongerThan5 = s -> isAllUpperCase(s) && s.length()>5;

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

Использование Predicate.and и Predicate.or полезно только в том случае, если у вас уже есть экземпляр Predicate и вы хотите расширить его логику. Но в этом случае у вас также есть экземпляр, на котором вы можете без проблем вызывать and или or.

person Holger    schedule 19.02.2015
comment
Кстати, вам вообще не нужен какой-либо служебный метод: StringUtils.isAllUpperCase(s) можно заменить на s.chars().allMatch(Character::isUpperCase). - person Holger; 19.02.2015