Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Stream API storing lambda expression as variable

This title sounds stupid even to me, but there must be at least somewhat clever way to achieve such effect and I don't know how else to explain it. I need to sort array using sorted in stream API. Here is my stream so far:

Arrays.stream(sequence.split(" "))
        .mapToInt(Integer::parseInt)
        .boxed()
        .sorted((a, b) -> a.compareTo(b))
        .forEach(a -> System.out.print(a + " "));

Now I have two different sorts of course - ascending and descending and the sort I need to use is specified in the user input. So what I want to do is having something like switch with 2 cases: "ascending" and "descending" and a variable to store the lambda expression respectively:

switch(command) {
    case "ascending": var = a.compareTo(b);
    case "descending": var = b.compareTo(a);
}

Then I my sorted looks like:

 .sorted((a, b) -> var)

I got the idea in a python course I attended. There it was available to store an object in variable, thus making the variable "executable". I realize that this lambda is not an object, but an expression, but I'm asking is there any clever way that can achieve such result, or should I just have

if(var)

and two diferent streams for each sort order.

like image 774
Alex Avatar asked Mar 31 '16 12:03

Alex


People also ask

How do you assign a lambda expression to a variable in Java?

Using Lambda ExpressionsLambda expressions can be stored in variables if the variable's type is an interface which has only one method. The lambda expression should have the same number of parameters and the same return type as that method.

Can a lambda function be assigned to a variable?

Creating a Lambda Function The lambda operator cannot have any statements and it returns a function object that we can assign to any variable.

Is it possible to modify source of stream of lambda expression in stream operation?

This is possible only if we can prevent interference with the data source during the execution of a stream pipeline. And the reason is : Modifying a stream's data source during execution of a stream pipeline can cause exceptions, incorrect answers, or nonconformant behavior.

How do you capture variables in lambda?

Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.


2 Answers

The question is not stupid at all. Answering it in a broader sense: Unfortunately, there is no generic solution for that. This is due to the type inference, which determines one particular type for the lambda expression, based on the target type. (The section about type inference may be helpful here, but does not cover all details regarding lambdas).

Particularly, a lambda like x -> y does not have any type. So there is no way of writing

GenericLambdaTypefunction = x -> y;

and later use function as a drop-in replacement for the actual lambda x -> y.

For example, when you have two functions like

static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }

you can call them both with the same lambda

useF(x -> true);
useP(x -> true);

but there is no way of "storing" the x -> true lambda in a way so that it later may be passed to both functions - you can only store it in a reference with the type that it will be needed in later:

Function<Integer, Boolean> f = x -> true;
Predicate<Integer>         p = x -> true;
useF(f);
useP(p);

For your particular case, the answer by Konstantin Yovkov already showed the solution: You have to store it as a Comparator<Integer> (ignoring the fact that you wouldn't have needed a lambda here in the first place...)

like image 90
Marco13 Avatar answered Oct 20 '22 21:10

Marco13


You can switch between using Comparator.reverseOrder() and Comparator.naturalOrder:

Comparator<Integer> comparator = youWantToHaveItReversed ? Comparator.reverseOrder(): Comparator.naturalOrder();
Arrays.stream(sequence.split(" "))
      .map(Integer::valueOf)
      .sorted(comparator)
      .forEach(a -> System.out.print(a + " "));
like image 25
Konstantin Yovkov Avatar answered Oct 20 '22 22:10

Konstantin Yovkov