Есть ли удобный метод для создания предиката, который проверяет, равно ли поле заданному значению?

Мне часто приходится фильтровать Stream или использовать предикат, который проверяет, имеет ли данное поле заданное значение.

Скажем, например, у меня есть этот POJO:

public class A {
    private Integer field;

    public A(final Integer field) {
        this.field = field;
    }

    public Integer getField() {
        return field;
    }
}

И я хочу отфильтровать Stream объектов на основе значения field:

    final Integer someValue = 42;
    Stream.of(new A(42), new A(1729), new A(42), new A(87539319), new A(null))
            .filter(a -> Objects.equals(a.getField(), someValue))
            ...

Будет ли удобный метод для создания предиката для метода filter? Я заметил, что есть Predicate.isEqual, но он не подходит.

Я мог бы довольно легко написать что-то вроде этого:

public static <T,R> Predicate<T> isEqual(Function<? super T, ? extends R> f, R value) {
    return v -> Objects.equals(value, f.apply(v));
}

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

    Stream.of(new A(42), new A(1729), new A(42), new A(87539319), new A(null))
            .filter(isEqual(A::getField, someValue))
            ...

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


person Didier L    schedule 23.10.2015    source источник
comment
Не то, что я знаю из. Но обратите внимание: если вы знаете, что someValue не является null, достаточно простого a -> someValue.equals(a.getField()). Мне он кажется не хуже isEqual(A::getField, someValue), особенно если учесть, что A это скорее NameOfARealLifeClass  -  person Holger    schedule 23.10.2015


Ответы (1)


Такого встроенного фабричного метода не существует, что можно легко проверить, просмотрев все случаи использования Predicate в JFC и найдите «Методы в… которые возвращают предикат». Помимо методов внутри самого Predicate , есть только Pattern.asPredicate() который возвращает Predicate.

Прежде чем вы собираетесь реализовать такой фабричный метод, вы должны спросить себя, действительно ли оно того стоит. Что делает ваше лямбда-выражение в .filter(a -> Objects.equals(a.getField(), someValue)) сложным, так это использование Objects.equals, которое не является необходимым, когда вы можете предсказать хотя бы по одному аргументу, является ли оно null. Поскольку здесь someValue никогда не бывает null, можно упростить выражение:

final Integer someValue = 42;
Stream.of(new A(42), new A(1729), new A(42), new A(87539319), new A(null))
    .filter(a -> someValue.equals(a.getField()))
    …

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

public static <T,R> Predicate<T> isEqual(Function<? super T, ? extends R> f, R value) {
    return f.andThen(Predicate.isEqual(value)::test)::apply;
}

Однако для производственного кода я бы рекомендовал такую ​​реализацию:

public static <T,R> Predicate<T> isEqual(Function<? super T, ? extends R> f, R value) {
    return value==null? t -> f.apply(t)==null: t -> value.equals(f.apply(t));
}

Это исключает тест на то, является ли константа null, чтобы упростить операцию, которая будет выполняться в каждом тесте. Так что Objects.equals все равно не нужен. Обратите внимание, что Predicate.isEqual делает то же самое.

person Holger    schedule 23.10.2015