3

I want to create Comparator based on Enum.name() which is inside a bean class:

List.of(new JobRsp(Job.MY), new JobRsp(Job.OUR), new JobRsp(Job.YOUR)).stream()
    .sorted(__ -> __.getJob().name());

If I only needed Enum.order() I could write sorted(Job::getJob).

Is it possible co compose zero args methods in functional style in Java? Something like:

FuncUtils.compose(Job::getJob, Enum::name)

Or probably even longer:

.sort(FuncUtils.chainGetters(ObjA::getB, ObjB::getC, ObjC::getD, ObjD::getE))

Imaginary chainGetters might check for nulls.

I understand that Java doesn't allow variadic generics so there should be chainGetters for each argument arity.

gavenkoa
  • 45,285
  • 19
  • 251
  • 303

1 Answers1

2

You have the default andThen method in Function interface that lets you chain functions.

Unfortunately there is no "clean" way to achieve the result, because to call a default method on a method reference you need to assign it, or cast it.

For example:

import static java.util.Comparator.comparing;

Function<Job, Enum> getJob = Job::getJob;
...
   .sorted(comparing(getJob.andThen(Enum::name)))

Or:

   .sorted(comparing( ((Function<Job, Enum>) Job::getJob).andThen(Enum::name) ))

So, it's cleaner to use a lambda expression using Comparator.comparing to create the comparator:

...
   .sorted(comparing(j -> j.getJob().name()))

The compose utility method that you suggest could be also implemented with:

public static <A, B, C>  Function<A, C> compose(Function<A, B> first, Function<B, C> second) {
    return first.andThen(second);
}

So you could use:

.sorted(comparing( FuncUtils.compose(Job::getJob, Enum::name)))
areus
  • 2,880
  • 2
  • 7
  • 17
  • _Unfortunately there is no "clean" way to achieve the result, because to call a default method on a method reference you need to assign it, or cast it._ -- Not really true. Use _type witness_: `Comparator.comparing(...)` https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html – terrorrussia-keeps-killing Jan 13 '21 at 12:42
  • 2
    What i meant is that you can't call: `(Job::getJob).andThen(Enum::name)` – areus Jan 13 '21 at 12:52
  • 2
    Okay, I missed _that_ point being confused for chaining comparators, and not function composition that can be implemented with a lambda expression. Anyway, there is another clean way omitting the cast and even introducing an intermediate typed variable by `static Function f(final Function f) { return f; }` and `f(ObjA::getObjB).andThen(ObjB::getObjC).andThen(ObjC::getObjD).andThen(ObjD::getObjE)`. I don't see a reason of having the `compose` method then. – terrorrussia-keeps-killing Jan 13 '21 at 13:17