I have this sample code that I wrote specifically for this question, but it reflects the real scenario that I came across 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 if lowercase with English ( EDIT: there are a dozen best and easiest ways to implement this, but this is just an example. In a real scenario, I filter a small data set based on arbitrary number of search criteria). I am going to use this lambda expression for several class methods.
Now suppose I want to have another locale that will be passed as an argument to a method that will use the lambda expression (and not the constructor). At work, this is not the language I have to deal with, but I defined its boundary as an immutable variable.
The simplest solution I could think of was to have the build method create this 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 that something is wrong with this approach. If I introduce an external immutable variable into a functional interface, I thought that perhaps I should pass it as an argument to the lambda expression instead of something?
When faced with a lambda expression that has an external immutable variable used in the expression, and that the immutable variable can be different for each use in the intermediate flow operation, is there a concrete approach that matches the well-known functional programming pattern?