Похоже, у вашего Action
класса есть пять полей, и вы хотите создать составной компаратор для всех полей, и это также нуль-безопасно. Вы могли бы сделать это:
Comparator<Action> COMPARATOR1 =
comparing(Action::date, nullsLast(naturalOrder()))
.thenComparing(Action::time, nullsLast(naturalOrder()))
.thenComparing(Action::foo, nullsLast(naturalOrder()))
.thenComparing(Action::bar, nullsLast(naturalOrder()))
.thenComparing(Action::baz, nullsLast(naturalOrder()));
Если повторение nullsLast(naturalOrder())
расстраивает, то можно создать несколько утилит, которые помогут. Во-первых, мы наблюдаем, что naturalOrder()
возвращает одноэлементный экземпляр Comparator. Это работает для любого сопоставимого типа; все, что он делает, это вызывает метод compareTo()
.
Оболочка nullsLast()
просто добавляет проверку на ноль перед передачей в свой упакованный компаратор. Это не зависит от типа, который он сравнивает. Это не синглтон, но любой компаратор нулевого-последнего-натурального порядка так же хорош, как и любой другой. Таким образом, мы можем создать один экземпляр и использовать его повторно. Мы заключаем его в метод, чтобы не приводить его к нужному типу на каждом сайте использования.
static final Comparator<?> NLNO = Comparator.nullsLast(Comparator.naturalOrder());
@SuppressWarnings("unchecked")
static <T extends Comparable<T>> Comparator<T> nlno() {
return (Comparator<T>)NLNO;
}
Это позволяет нам делать следующее:
Comparator<Action> COMPARATOR2 =
comparing(Action::date, nlno())
.thenComparing(Action::time, nlno())
.thenComparing(Action::foo, nlno())
.thenComparing(Action::bar, nlno())
.thenComparing(Action::baz, nlno());
Все еще слишком многословен? Мы можем написать метод, который принимает переменное число методов доступа и создает на их основе комбинированный компаратор, сократив более чем thenComposing()
. Это выглядит так:
@SafeVarargs
@SuppressWarnings("varargs")
static <C extends Comparable<C>> Comparator<Action>
comparatorWith(Function<Action, ? extends C>... extractors) {
return Arrays.stream(extractors)
.map(ex -> comparing(ex, nullsLast(naturalOrder())))
.reduce(Comparator::thenComparing)
.orElseThrow(() -> new IllegalArgumentException("need at least one extractor"));
}
Это позволяет нам написать следующее:
Comparator<Action> COMPARATOR3 =
comparatorWith(Action::date, Action::time, Action::foo, Action::bar, Action::baz);
Стоит ли это того? Вспомогательный метод, вероятно, более сложен, чем запись одной цепочки nullsLast(naturalOrder())
вызовов. Однако если вам нужна куча различных цепочек компараторов, то возможность повторного использования вспомогательного метода может стоить его сложности.