Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expressions with external immutable variables used within the expression

Tags:

java

lambda

I have this example code that I've written specifically for this question, but it reflects a real scenario I was confronted with at work:

List<String> names = Arrays.asList("ALICE", "Alice", "BOB", "Bob", "CHUCK", "Chuck");

Predicate<String> has_u_or_i_whenLowercased = Stream.of("u", "i")
        .map(bit -> (Predicate<String>) (source -> source.toLowerCase(Locale.ENGLISH).contains(bit)))
        .reduce(Predicate::or)
        .orElse(p -> false);

List<String> english = names.stream()
        .filter(has_u_or_i_whenLowercased)
        .collect(Collectors.toList());
System.out.println(english);
System.out.println(english.size());

It creates a predicate that checks if the source String contains u or i when lowercased with the English locale (EDIT: There are a dozen better and simpler ways to implement this, but this is just an example. In the real scenario I am filtering a small data set based on an arbitrary number of search criteria). I'm going to use this lambda expression across several methods of a class.

Now, assume I want to have a different locale, which will be passed as an argument to the method that will use the lambda expression (not the constructor). At work, it's not a Locale I have to deal with, but I defined its boundary as being an immutable variable.

The simplest solution I could think of was to have a method "build" that lambda expression.

@Override
public void run() {
    List<String> names = Arrays.asList("ALICE", "Alice", "BOB", "Bob", "CHUCK", "Chuck");

    List<String> english = names.stream()
            .filter(createUIPredicate(Locale.ENGLISH))
            .collect(Collectors.toList());
    System.out.println(english);
    System.out.println(english.size());

    System.out.println("--");

    List<String> turkish = names.stream()
            .filter(createUIPredicate(new Locale("tr", "TR")))
            .collect(Collectors.toList());
    System.out.println(turkish);
    System.out.println(turkish.size());
}

private Predicate<String> createUIPredicate(Locale locale) {
    return Stream.of("u", "i")
            .map(bit -> (Predicate<String>) (source -> source.toLowerCase(locale).contains(bit)))
            .reduce(Predicate::or)
            .orElse(p -> false);
}

However I feel like there is something wrong with this approach. If I am injecting an external immutable variable into a functional interface, I thought maybe I'm supposed to pass it as an lambda expression argument instead somewhere?

When confronted with a lambda expression which has an external immutable variable used within the expression, and that immutable variable may be different for every use within a stream intermediate operation, is there a specific approach that matches a known functional programming pattern?

like image 754
Hay Avatar asked Nov 08 '22 11:11

Hay


1 Answers

There's not really much practical difference between your method solution and your lambda solution from the comments, both take advantage of lambdas "closing over" "effectively final" variables. Both are pretty common in the Java 8 functional code I've written.

private Predicate<String> build(Locale locale) {
  return str -> str.toLowerCase(locale);
}

versus:

Function<Locale, Predicate<String>> build = locale -> str -> str.toLowerCase(locale);

The decision between the two is just one of style preference and/or whether that builder is used only within a single method or in multiple places in your class.

like image 171
ajoberstar Avatar answered Nov 14 '22 22:11

ajoberstar