What makes an interface work with lambdas? It must have exactly one abstract method - a functional interface. Java provides Function, Predicate, Consumer, and more. You can create your own too.

Define a functional interface

Interface with exactly one abstract method.

sample
Definition.java
Replay: real traced execution (multi-file project)
public class Definition {

    @FunctionalInterface
    interface Formatter {
        String format(String input);

        default String formatOrEmpty(String input) {
            if (input == null) return "";
            return format(input);
        }

        static Formatter identity() {
            return s -> s;
        }
    }

    public static void main(String[] args) {
        Formatter upper = s -> s.toUpperCase();
        String sample = "hello";
        System.out.println("upper: " + upper.format(sample));

        System.out.println("default: '" + upper.formatOrEmpty(null) + "'");

        Formatter id = Formatter.identity();
        System.out.println("identity: " + id.format("same"));

    }
}
public class Definition {

    @FunctionalInterface
    interface Formatter {
        String format(String input);

        default String formatOrEmpty(String input) {
            if (input == null) return "";
            return format(input);
        }

        static Formatter identity() {
            return s -> s;
        }
    }

    public static void main(String[] args) {
        Formatter upper = s -> s.toUpperCase();
        String sample = "lambda";
        System.out.println("upper: " + upper.format(sample));

        System.out.println("default: '" + upper.formatOrEmpty(null) + "'");

        Formatter id = Formatter.identity();
        System.out.println("identity: " + id.format("same"));

    }
}
public class Definition {

    @FunctionalInterface
    interface Formatter {
        String format(String input);

        default String formatOrEmpty(String input) {
            if (input == null) return "";
            return format(input);
        }

        static Formatter identity() {
            return s -> s;
        }
    }

    public static void main(String[] args) {
        Formatter upper = s -> s.toUpperCase();
        String sample = "Java";
        System.out.println("upper: " + upper.format(sample));

        System.out.println("default: '" + upper.formatOrEmpty(null) + "'");

        Formatter id = Formatter.identity();
        System.out.println("identity: " + id.format("same"));

    }
}
  1. upper ← ⟨Definition lambda A⟩, sample ← hello

    17public static void main(String[] args) {18    Formatter upper→ ⟨Definition lambda A⟩ = s -> s.toUpperCase();19    String sample→ hello = "hello"; //@sample="hello", "lambda", "Java"20    System.out.println("upper: " + upper.format(samplehello));2122    System.out.println("default: '" + upper.formatOrEmpty(null) + "'");
    outputupper: HELLO
  2. default String formatOrEmpty(String input)

    7default String formatOrEmpty(String inputnull) {8    if (input == null) return "";
  3. if (input == null)

    7default String formatOrEmpty(String input) {8    if (inputnull == null) return "";9    return format(input);
  4. System.out.println("default: '" + upper.formatOrEmpty(null) + "'");

    22System.out.println("default: '" + upper.formatOrEmpty(null) + "'");2324Formatter id = Formatter.identity();25System.out.println("identity: " + id.format("same"));
    outputdefault: ''
  5. id ← ⟨Definition$Formatter lambda B⟩

    24Formatter id→ ⟨Definition$Formatter lambda B⟩ = Formatter.identity();25System.out.println("identity: " + id.format("same"));
    outputidentity: same
  1. upper ← ⟨Definition lambda A⟩, sample ← lambda

    17public static void main(String[] args) {18    Formatter upper→ ⟨Definition lambda A⟩ = s -> s.toUpperCase();19    String sample→ lambda = "lambda";20    System.out.println("upper: " + upper.format(samplelambda));2122    System.out.println("default: '" + upper.formatOrEmpty(null) + "'");
    outputupper: LAMBDA
  2. default String formatOrEmpty(String input)

    7default String formatOrEmpty(String inputnull) {8    if (input == null) return "";
  3. if (input == null)

    7default String formatOrEmpty(String input) {8    if (inputnull == null) return "";9    return format(input);
  4. System.out.println("default: '" + upper.formatOrEmpty(null) + "'");

    22System.out.println("default: '" + upper.formatOrEmpty(null) + "'");2324Formatter id = Formatter.identity();25System.out.println("identity: " + id.format("same"));
    outputdefault: ''
  5. id ← ⟨Definition$Formatter lambda B⟩

    24Formatter id→ ⟨Definition$Formatter lambda B⟩ = Formatter.identity();25System.out.println("identity: " + id.format("same"));
    outputidentity: same
  1. upper ← ⟨Definition lambda A⟩, sample ← Java

    17public static void main(String[] args) {18    Formatter upper→ ⟨Definition lambda A⟩ = s -> s.toUpperCase();19    String sample→ Java = "Java";20    System.out.println("upper: " + upper.format(sampleJava));2122    System.out.println("default: '" + upper.formatOrEmpty(null) + "'");
    outputupper: JAVA
  2. default String formatOrEmpty(String input)

    7default String formatOrEmpty(String inputnull) {8    if (input == null) return "";
  3. if (input == null)

    7default String formatOrEmpty(String input) {8    if (inputnull == null) return "";9    return format(input);
  4. System.out.println("default: '" + upper.formatOrEmpty(null) + "'");

    22System.out.println("default: '" + upper.formatOrEmpty(null) + "'");2324Formatter id = Formatter.identity();25System.out.println("identity: " + id.format("same"));
    outputdefault: ''
  5. id ← ⟨Definition$Formatter lambda B⟩

    24Formatter id→ ⟨Definition$Formatter lambda B⟩ = Formatter.identity();25System.out.println("identity: " + id.format("same"));
    outputidentity: same

@FunctionalInterface annotation ensures single abstract method.

functional interface Interface with one abstract method. Target type for lambda expressions.

Built-in functional interfaces

Java's standard functional interfaces.

Builtin.java
Replay: real traced execution (multi-file project)
import java.util.function.*;

public class Builtin {
    public static void main(String[] args) {
        Predicate<String> isLong = s -> s.length() >= 5;
        System.out.println("isLong('cat') = " + isLong.test("cat"));
        System.out.println("isLong('tiger') = " + isLong.test("tiger"));

        Function<String, Integer> length = s -> s.length();
        System.out.println("length('hello') = " + length.apply("hello"));

        Consumer<String> printer = s -> System.out.println("print: " + s);
        printer.accept("message");

        long fixedMillis = 1700000000000L;
        Supplier<Long> nowMillis = () -> fixedMillis;
        System.out.println("nowMillis() = " + nowMillis.get());

        UnaryOperator<String> trim = s -> s.trim();
        System.out.println("trim('  hi  ') = '" + trim.apply("  hi  ") + "'");

        BiFunction<Integer, Integer, Integer> max = (a, b) -> a > b ? a : b;
        System.out.println("max(3, 7) = " + max.apply(3, 7));

    }
}
  1. isLong ← ⟨Builtin lambda A⟩, length ← ⟨Builtin lambda B⟩, printer ← ⟨Builtin lambda C⟩

    3public class Builtin {4    public static void main(String[] args) {5        Predicate<String> isLong→ ⟨Builtin lambda A⟩ = s -> s.length() >= 5;6        System.out.println("isLong('cat') = " + isLong.test("cat"));7        System.out.println("isLong('tiger') = " + isLong.test("tiger"));89        Function<String, Integer> length→ ⟨Builtin lambda B⟩ = s -> s.length();10        System.out.println("length('hello') = " + length.apply("hello"));1112        Consumer<String> printer→ ⟨Builtin lambda C⟩ = s -> System.out.println("print: " + s);13        printer.accept("message");1415        long fixedMillis→ 1700000000000 = 1700000000000L;16        Supplier<Long> nowMillis→ ⟨Builtin lambda D⟩ = () -> fixedMillis;17        System.out.println("nowMillis() = " + nowMillis.get());1819        UnaryOperator<String> trim→ ⟨Builtin lambda E⟩ = s -> s.trim();20        System.out.println("trim('  hi  ') = '" + trim.apply("  hi  ") + "'");2122        BiFunction<Integer, Integer, Integer> max→ ⟨Builtin lambda F⟩ = (a, b) -> a > b ? a : b;23        System.out.println("max(3, 7) = " + max.apply(3, 7));
    outputisLong('cat') = false
    isLong('tiger') = true
    length('hello') = 5
    nowMillis() = 1700000000000
    trim('  hi  ') = 'hi'
    max(3, 7) = 7

Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T> - learn these.

Function `Function<T,R>`: takes T, returns R. Method: `R apply(T t)`.
Predicate `Predicate<T>`: takes T, returns boolean. Method: `boolean test(T t)`.

Function composition

Chain functions together.

text
Composition.java
Replay: real traced execution (multi-file project)
import java.util.function.*;

public class Composition {
    public static void main(String[] args) {
        Function<String, String> trim = s -> s.trim();
        Function<String, String> upper = s -> s.toUpperCase();
        Function<String, Integer> length = s -> s.length();

        String text = "  hi ";
        Function<String, String> upperAfterTrim = upper.compose(trim);
        System.out.println("upperAfterTrim('" + text + "') = " + upperAfterTrim.apply(text));

        Function<String, Integer> lenAfterUpperTrim = upperAfterTrim.andThen(length);
        System.out.println("lenAfterUpperTrim('" + text + "') = " + lenAfterUpperTrim.apply(text));

        Predicate<String> nonEmpty = s -> !s.isEmpty();
        Predicate<String> startsWithA = s -> s.startsWith("A");

        Predicate<String> ok = nonEmpty.and(startsWithA);
        System.out.println("ok('') = " + ok.test(""));
        System.out.println("ok('Bob') = " + ok.test("Bob"));
        System.out.println("ok('Alice') = " + ok.test("Alice"));

        System.out.println("not startsWithA('Bob') = " + startsWithA.negate().test("Bob"));

        Consumer<String> a = s -> System.out.print("[" + s + "]");
        Consumer<String> b = s -> System.out.println(" (len=" + s.length() + ")");
        Consumer<String> both = a.andThen(b);
        both.accept("hello");

    }
}
import java.util.function.*;

public class Composition {
    public static void main(String[] args) {
        Function<String, String> trim = s -> s.trim();
        Function<String, String> upper = s -> s.toUpperCase();
        Function<String, Integer> length = s -> s.length();

        String text = " Alice ";
        Function<String, String> upperAfterTrim = upper.compose(trim);
        System.out.println("upperAfterTrim('" + text + "') = " + upperAfterTrim.apply(text));

        Function<String, Integer> lenAfterUpperTrim = upperAfterTrim.andThen(length);
        System.out.println("lenAfterUpperTrim('" + text + "') = " + lenAfterUpperTrim.apply(text));

        Predicate<String> nonEmpty = s -> !s.isEmpty();
        Predicate<String> startsWithA = s -> s.startsWith("A");

        Predicate<String> ok = nonEmpty.and(startsWithA);
        System.out.println("ok('') = " + ok.test(""));
        System.out.println("ok('Bob') = " + ok.test("Bob"));
        System.out.println("ok('Alice') = " + ok.test("Alice"));

        System.out.println("not startsWithA('Bob') = " + startsWithA.negate().test("Bob"));

        Consumer<String> a = s -> System.out.print("[" + s + "]");
        Consumer<String> b = s -> System.out.println(" (len=" + s.length() + ")");
        Consumer<String> both = a.andThen(b);
        both.accept("hello");

    }
}
import java.util.function.*;

public class Composition {
    public static void main(String[] args) {
        Function<String, String> trim = s -> s.trim();
        Function<String, String> upper = s -> s.toUpperCase();
        Function<String, Integer> length = s -> s.length();

        String text = "Bob";
        Function<String, String> upperAfterTrim = upper.compose(trim);
        System.out.println("upperAfterTrim('" + text + "') = " + upperAfterTrim.apply(text));

        Function<String, Integer> lenAfterUpperTrim = upperAfterTrim.andThen(length);
        System.out.println("lenAfterUpperTrim('" + text + "') = " + lenAfterUpperTrim.apply(text));

        Predicate<String> nonEmpty = s -> !s.isEmpty();
        Predicate<String> startsWithA = s -> s.startsWith("A");

        Predicate<String> ok = nonEmpty.and(startsWithA);
        System.out.println("ok('') = " + ok.test(""));
        System.out.println("ok('Bob') = " + ok.test("Bob"));
        System.out.println("ok('Alice') = " + ok.test("Alice"));

        System.out.println("not startsWithA('Bob') = " + startsWithA.negate().test("Bob"));

        Consumer<String> a = s -> System.out.print("[" + s + "]");
        Consumer<String> b = s -> System.out.println(" (len=" + s.length() + ")");
        Consumer<String> both = a.andThen(b);
        both.accept("hello");

    }
}
  1. trim ← ⟨Composition lambda A⟩, upper ← ⟨Composition lambda B⟩

    3public class Composition {4    public static void main(String[] args) {5        Function<String, String> trim→ ⟨Composition lambda A⟩ = s -> s.trim();6        Function<String, String> upper→ ⟨Composition lambda B⟩ = s -> s.toUpperCase();7        Function<String, Integer> length→ ⟨Composition lambda C⟩ = s -> s.length();89        String text→   hi  = "  hi "; //@text="  hi ", " Alice ", "Bob"10        Function<String, String> upperAfterTrim→ ⟨Function lambda D⟩ = upper.compose(trim⟨Composition lambda A⟩);11        System.out.println("upperAfterTrim('" + text  hi  + "') = " + upperAfterTrim.apply(text));1213        Function<String, Integer> lenAfterUpperTrim→ ⟨Function lambda E⟩ = upperAfterTrim.andThen(length⟨Composition lambda C⟩);14        System.out.println("lenAfterUpperTrim('" + text  hi  + "') = " + lenAfterUpperTrim.apply(text));1516        Predicate<String> nonEmpty→ ⟨Composition lambda F⟩ = s -> !s.isEmpty();17        Predicate<String> startsWithA→ ⟨Composition lambda G⟩ = s -> s.startsWith("A");1819        Predicate<String> ok→ ⟨Predicate lambda H⟩ = nonEmpty.and(startsWithA⟨Composition lambda G⟩);20        System.out.println("ok('') = " + ok.test(""));21        System.out.println("ok('Bob') = " + ok.test("Bob"));22        System.out.println("ok('Alice') = " + ok.test("Alice"));2324        System.out.println("not startsWithA('Bob') = " + startsWithA.negate().test("Bob"));2526        Consumer<String> a→ ⟨Composition lambda I⟩ = s -> System.out.print("[" + s + "]");27        Consumer<String> b→ ⟨Composition lambda J⟩ = s -> System.out.println(" (len=" + s.length() + ")");28        Consumer<String> both→ ⟨Consumer lambda K⟩ = a.andThen(b⟨Composition lambda J⟩);29        both.accept("hello");
    outputupperAfterTrim('  hi ') = HI
    lenAfterUpperTrim('  hi ') = 2
    ok('') = false
    ok('Bob') = false
    ok('Alice') = true
    not startsWithA('Bob') = true
  1. trim ← ⟨Composition lambda A⟩, upper ← ⟨Composition lambda B⟩

    3public class Composition {4    public static void main(String[] args) {5        Function<String, String> trim→ ⟨Composition lambda A⟩ = s -> s.trim();6        Function<String, String> upper→ ⟨Composition lambda B⟩ = s -> s.toUpperCase();7        Function<String, Integer> length→ ⟨Composition lambda C⟩ = s -> s.length();89        String text→  Alice  = " Alice ";10        Function<String, String> upperAfterTrim→ ⟨Function lambda D⟩ = upper.compose(trim⟨Composition lambda A⟩);11        System.out.println("upperAfterTrim('" + text Alice  + "') = " + upperAfterTrim.apply(text));1213        Function<String, Integer> lenAfterUpperTrim→ ⟨Function lambda E⟩ = upperAfterTrim.andThen(length⟨Composition lambda C⟩);14        System.out.println("lenAfterUpperTrim('" + text Alice  + "') = " + lenAfterUpperTrim.apply(text));1516        Predicate<String> nonEmpty→ ⟨Composition lambda F⟩ = s -> !s.isEmpty();17        Predicate<String> startsWithA→ ⟨Composition lambda G⟩ = s -> s.startsWith("A");1819        Predicate<String> ok→ ⟨Predicate lambda H⟩ = nonEmpty.and(startsWithA⟨Composition lambda G⟩);20        System.out.println("ok('') = " + ok.test(""));21        System.out.println("ok('Bob') = " + ok.test("Bob"));22        System.out.println("ok('Alice') = " + ok.test("Alice"));2324        System.out.println("not startsWithA('Bob') = " + startsWithA.negate().test("Bob"));2526        Consumer<String> a→ ⟨Composition lambda I⟩ = s -> System.out.print("[" + s + "]");27        Consumer<String> b→ ⟨Composition lambda J⟩ = s -> System.out.println(" (len=" + s.length() + ")");28        Consumer<String> both→ ⟨Consumer lambda K⟩ = a.andThen(b⟨Composition lambda J⟩);29        both.accept("hello");
    outputupperAfterTrim(' Alice ') = ALICE
    lenAfterUpperTrim(' Alice ') = 5
    ok('') = false
    ok('Bob') = false
    ok('Alice') = true
    not startsWithA('Bob') = true
  1. trim ← ⟨Composition lambda A⟩, upper ← ⟨Composition lambda B⟩

    3public class Composition {4    public static void main(String[] args) {5        Function<String, String> trim→ ⟨Composition lambda A⟩ = s -> s.trim();6        Function<String, String> upper→ ⟨Composition lambda B⟩ = s -> s.toUpperCase();7        Function<String, Integer> length→ ⟨Composition lambda C⟩ = s -> s.length();89        String text→ Bob = "Bob";10        Function<String, String> upperAfterTrim→ ⟨Function lambda D⟩ = upper.compose(trim⟨Composition lambda A⟩);11        System.out.println("upperAfterTrim('" + textBob + "') = " + upperAfterTrim.apply(text));1213        Function<String, Integer> lenAfterUpperTrim→ ⟨Function lambda E⟩ = upperAfterTrim.andThen(length⟨Composition lambda C⟩);14        System.out.println("lenAfterUpperTrim('" + textBob + "') = " + lenAfterUpperTrim.apply(text));1516        Predicate<String> nonEmpty→ ⟨Composition lambda F⟩ = s -> !s.isEmpty();17        Predicate<String> startsWithA→ ⟨Composition lambda G⟩ = s -> s.startsWith("A");1819        Predicate<String> ok→ ⟨Predicate lambda H⟩ = nonEmpty.and(startsWithA⟨Composition lambda G⟩);20        System.out.println("ok('') = " + ok.test(""));21        System.out.println("ok('Bob') = " + ok.test("Bob"));22        System.out.println("ok('Alice') = " + ok.test("Alice"));2324        System.out.println("not startsWithA('Bob') = " + startsWithA.negate().test("Bob"));2526        Consumer<String> a→ ⟨Composition lambda I⟩ = s -> System.out.print("[" + s + "]");27        Consumer<String> b→ ⟨Composition lambda J⟩ = s -> System.out.println(" (len=" + s.length() + ")");28        Consumer<String> both→ ⟨Consumer lambda K⟩ = a.andThen(b⟨Composition lambda J⟩);29        both.accept("hello");
    outputupperAfterTrim('Bob') = BOB
    lenAfterUpperTrim('Bob') = 3
    ok('') = false
    ok('Bob') = false
    ok('Alice') = true
    not startsWithA('Bob') = true

f.andThen(g) - apply f, then g. f.compose(g) - apply g, then f.

Custom generic functional interface

Create your own functional interfaces.

CustomGeneric.java
Replay: real traced execution (multi-file project)
import java.util.*;

public class CustomGeneric {

    @FunctionalInterface
    interface Validator<T> {
        boolean isValid(T value);

        default Validator<T> and(Validator<T> other) {
            return v -> this.isValid(v) && other.isValid(v);
        }

        default Validator<T> or(Validator<T> other) {
            return v -> this.isValid(v) || other.isValid(v);
        }
    }

    private static <T> List<T> filter(List<T> items, Validator<T> validator) {
        List<T> out = new ArrayList<>();
        for (T item : items) {
            if (validator.isValid(item)) {
                out.add(item);
            }
        }
        return out;
    }

    public static void main(String[] args) {
        Validator<String> nonEmpty = s -> s != null && !s.isEmpty();
        Validator<String> hasAt = s -> s.contains("@");

        Validator<String> emailLike = nonEmpty.and(hasAt);

        List<String> inputs = Arrays.asList("", "alice", "alice@example.com", "@", "bob@x");
        System.out.println("inputs: " + inputs);
        System.out.println("emailLike: " + filter(inputs, emailLike));

        Validator<String> empty = s -> s.isEmpty();
        Validator<String> emptyOrEmail = empty.or(emailLike);
        System.out.println("emptyOrEmail: " + filter(inputs, emptyOrEmail));

    }
}
  1. nonEmpty ← ⟨CustomGeneric lambda A⟩, hasAt ← ⟨CustomGeneric lambda B⟩

    28public static void main(String[] args) {29    Validator<String> nonEmpty→ ⟨CustomGeneric lambda A⟩ = s -> s != null && !s.isEmpty();30    Validator<String> hasAt→ ⟨CustomGeneric lambda B⟩ = s -> s.contains("@");3132    Validator<String> emailLike = nonEmpty.and(hasAt⟨CustomGeneric lambda B⟩);
  2. default Validator<T> and(Validator<T> other)

    9default Validator<T> and(Validator<T> other⟨CustomGeneric lambda B⟩) {10    return v -> this.isValid(v) && other.isValid(v);11}
  3. emailLike ← ⟨CustomGeneric$Validator lambda C⟩, inputs ← [, alice, alice@example.com, @, bob@x]

    32Validator<String> emailLike→ ⟨CustomGeneric$Validator lambda C⟩ = nonEmpty.and(hasAt⟨CustomGeneric lambda B⟩);3334List<String> inputs→ [, alice, alice@example.com, @, bob@x] = Arrays.asList("", "alice", "alice@example.com", "@", "bob@x");35System.out.println("inputs: " + inputs[, alice, alice@example.com, @, bob@x]);36System.out.println("emailLike: " + filter(inputs[, alice, alice@example.com, @, bob@x], emailLike⟨CustomGeneric$Validator lambda C⟩));
    outputinputs: [, alice, alice@example.com, @, bob@x]
  4. out ← []

    pass 1 of 2
    18private static <T> List<T> filter(List<T> items[, alice, alice@example.com, @, bob@x], Validator<T> validator⟨CustomGeneric$Validator lambda C⟩) {19    List<T> out→ [] = new ArrayList<>();20    for (T item : items) {
  5. for (T item : items)

    pass 1 of 10
    19List<T> out = new ArrayList<>();20for (T item(empty) : items[, alice, alice@example.com, @, bob@x]) {21    if (validator.isValid(item)) {
    All 10 passes — pass 1 is the card above
    passitem
    1(empty)
    2alice
    3alice@example.com
    4@
    5bob@x
    6(empty)
    7alice
    8alice@example.com
    9@
    10bob@x
  6. if (validator.isValid(item))

    pass 1 of 7
    20for (T item : items) {21    if (validator.isValid(itemalice@example.com)) {22        out.add(itemalice@example.com);23    }
    All 7 passes — pass 1 is the card above
    passitem
    1alice@example.com
    2@
    3bob@x
    4(empty)
    5alice@example.com
    6@
    7bob@x
  7. return out;

    24    }25    return out[alice@example.com, @, bob@x];26}
  8. empty ← ⟨CustomGeneric lambda D⟩

    35System.out.println("inputs: " + inputs);36System.out.println("emailLike: " + filter(inputs[, alice, alice@example.com, @, bob@x], emailLike⟨CustomGeneric$Validator lambda C⟩));3738Validator<String> empty→ ⟨CustomGeneric lambda D⟩ = s -> s.isEmpty();39Validator<String> emptyOrEmail = empty.or(emailLike⟨CustomGeneric$Validator lambda C⟩);40System.out.println("emptyOrEmail: " + filter(inputs, emptyOrEmail));
    outputemailLike: [alice@example.com, @, bob@x]
  9. default Validator<T> or(Validator<T> other)

    13default Validator<T> or(Validator<T> other⟨CustomGeneric$Validator lambda C⟩) {14    return v -> this.isValid(v) || other.isValid(v);15}
  10. emptyOrEmail ← ⟨CustomGeneric$Validator lambda E⟩

    38Validator<String> empty = s -> s.isEmpty();39Validator<String> emptyOrEmail→ ⟨CustomGeneric$Validator lambda E⟩ = empty.or(emailLike⟨CustomGeneric$Validator lambda C⟩);40System.out.println("emptyOrEmail: " + filter(inputs[, alice, alice@example.com, @, bob@x], emptyOrEmail⟨CustomGeneric$Validator lambda E⟩));
  11. out ← []

    pass 2 of 2
    18private static <T> List<T> filter(List<T> items[, alice, alice@example.com, @, bob@x], Validator<T> validator⟨CustomGeneric$Validator lambda E⟩) {19    List<T> out→ [] = new ArrayList<>();20    for (T item : items) {
  12. return out;

    24    }25    return out[, alice@example.com, @, bob@x];26}
  13. System.out.println("emptyOrEmail: " + filter(inputs, emptyOrEmail));

    39Validator<String> emptyOrEmail = empty.or(emailLike);40System.out.println("emptyOrEmail: " + filter(inputs[, alice, alice@example.com, @, bob@x], emptyOrEmail⟨CustomGeneric$Validator lambda E⟩));
    outputemptyOrEmail: [, alice@example.com, @, bob@x]

Define with generics for maximum reusability.

Pass behavior as parameter

Methods that accept functional interfaces.

minLength
PassBehavior.java
Replay: real traced execution (multi-file project)
import java.util.*;
import java.util.function.*;

public class PassBehavior {

    private static <T, R> List<R> map(List<T> items, Function<T, R> fn) {
        List<R> out = new ArrayList<>();
        for (T item : items) {
            out.add(fn.apply(item));
        }
        return out;
    }

    private static <T> List<T> filter(List<T> items, Predicate<T> pred) {
        List<T> out = new ArrayList<>();
        for (T item : items) {
            if (pred.test(item)) {
                out.add(item);
            }
        }
        return out;
    }

    private static int reduceInts(List<Integer> items, int identity, IntBinaryOperator op) {
        int acc = identity;
        for (int v : items) {
            acc = op.applyAsInt(acc, v);
        }
        return acc;
    }

    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        System.out.println("lengths: " + map(names, s -> s.length()));

        int minLength = 5;
        System.out.println("long names: " + filter(names, s -> s.length() >= minLength));

        List<Integer> nums = Arrays.asList(1, 2, 3, 4);
        int sum = reduceInts(nums, 0, (a, b) -> a + b);
        int product = reduceInts(nums, 1, (a, b) -> a * b);
        System.out.println("sum = " + sum);
        System.out.println("product = " + product);

    }
}
import java.util.*;
import java.util.function.*;

public class PassBehavior {

    private static <T, R> List<R> map(List<T> items, Function<T, R> fn) {
        List<R> out = new ArrayList<>();
        for (T item : items) {
            out.add(fn.apply(item));
        }
        return out;
    }

    private static <T> List<T> filter(List<T> items, Predicate<T> pred) {
        List<T> out = new ArrayList<>();
        for (T item : items) {
            if (pred.test(item)) {
                out.add(item);
            }
        }
        return out;
    }

    private static int reduceInts(List<Integer> items, int identity, IntBinaryOperator op) {
        int acc = identity;
        for (int v : items) {
            acc = op.applyAsInt(acc, v);
        }
        return acc;
    }

    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        System.out.println("lengths: " + map(names, s -> s.length()));

        int minLength = 3;
        System.out.println("long names: " + filter(names, s -> s.length() >= minLength));

        List<Integer> nums = Arrays.asList(1, 2, 3, 4);
        int sum = reduceInts(nums, 0, (a, b) -> a + b);
        int product = reduceInts(nums, 1, (a, b) -> a * b);
        System.out.println("sum = " + sum);
        System.out.println("product = " + product);

    }
}
import java.util.*;
import java.util.function.*;

public class PassBehavior {

    private static <T, R> List<R> map(List<T> items, Function<T, R> fn) {
        List<R> out = new ArrayList<>();
        for (T item : items) {
            out.add(fn.apply(item));
        }
        return out;
    }

    private static <T> List<T> filter(List<T> items, Predicate<T> pred) {
        List<T> out = new ArrayList<>();
        for (T item : items) {
            if (pred.test(item)) {
                out.add(item);
            }
        }
        return out;
    }

    private static int reduceInts(List<Integer> items, int identity, IntBinaryOperator op) {
        int acc = identity;
        for (int v : items) {
            acc = op.applyAsInt(acc, v);
        }
        return acc;
    }

    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        System.out.println("lengths: " + map(names, s -> s.length()));

        int minLength = 7;
        System.out.println("long names: " + filter(names, s -> s.length() >= minLength));

        List<Integer> nums = Arrays.asList(1, 2, 3, 4);
        int sum = reduceInts(nums, 0, (a, b) -> a + b);
        int product = reduceInts(nums, 1, (a, b) -> a * b);
        System.out.println("sum = " + sum);
        System.out.println("product = " + product);

    }
}
  1. names ← [Alice, Bob, Charlie]

    32public static void main(String[] args) {33    List<String> names→ [Alice, Bob, Charlie] = Arrays.asList("Alice", "Bob", "Charlie");3435    System.out.println("lengths: " + map(names[Alice, Bob, Charlie], s -> s.length()));
  2. out ← []

    6private static <T, R> List<R> map(List<T> items[Alice, Bob, Charlie], Function<T, R> fn⟨PassBehavior lambda A⟩) {7    List<R> out→ [] = new ArrayList<>();8    for (T item : items) {
  3. for (T item : items)

    pass 1 of 3
    7List<R> out = new ArrayList<>();8for (T itemAlice : items[Alice, Bob, Charlie]) {9    out.add(fn.apply(itemAlice));10}
    All 3 passes — pass 1 is the card above
    passitem
    1Alice
    2Bob
    3Charlie
  4. return out;

    10    }11    return out[5, 3, 7];12}
  5. minLength ← 5

    35System.out.println("lengths: " + map(names[Alice, Bob, Charlie], s -> s.length()));3637int minLength→ 5 = 5; //@minLength=5, 3, 738System.out.println("long names: " + filter(names[Alice, Bob, Charlie], s -> s.length() >= minLength));
    outputlengths: [5, 3, 7]
  6. out ← []

    14private static <T> List<T> filter(List<T> items[Alice, Bob, Charlie], Predicate<T> pred⟨PassBehavior lambda B⟩) {15    List<T> out→ [] = new ArrayList<>();16    for (T item : items) {
  7. for (T item : items)

    pass 1 of 3
    15List<T> out = new ArrayList<>();16for (T itemAlice : items[Alice, Bob, Charlie]) {17    if (pred.test(item)) {
    All 3 passes — pass 1 is the card above
    passitem
    1Alice
    2Bob
    3Charlie
  8. if (pred.test(item))

    pass 1 of 2
    16for (T item : items) {17    if (pred.test(itemAlice)) {18        out.add(itemAlice);19    }
  9. if (pred.test(item))

    pass 2 of 2
    16for (T item : items) {17    if (pred.test(itemCharlie)) {18        out.add(itemCharlie);19    }
  10. return out;

    20    }21    return out[Alice, Charlie];22}
  11. nums ← [1, 2, 3, 4]

    37int minLength = 5; //@minLength=5, 3, 738System.out.println("long names: " + filter(names[Alice, Bob, Charlie], s -> s.length() >= minLength));3940List<Integer> nums→ [1, 2, 3, 4] = Arrays.asList(1, 2, 3, 4);41int sum = reduceInts(nums[1, 2, 3, 4], 0, (a, b) -> a + b);42int product = reduceInts(nums, 1, (a, b) -> a * b);
    outputlong names: [Alice, Charlie]
  12. acc ← 0

    pass 1 of 2
    24private static int reduceInts(List<Integer> items[1, 2, 3, 4], int identity0, IntBinaryOperator op⟨PassBehavior lambda C⟩) {25    int acc→ 0 = identity;26    for (int v : items) {
  13. acc ← 1

    pass 1 of 8
    25int acc = identity;26for (int v1 : items[1, 2, 3, 4]) {27    acc→ 1 = op.applyAsInt(acc, v1);28}
    All 8 passes — pass 1 is the card above
    passvacc
    110 1
    221 3
    333 6
    446 10
    511
    621 2
    732 6
    846 24
  14. return acc;

    28    }29    return acc10;30}
  15. sum ← 10

    40List<Integer> nums = Arrays.asList(1, 2, 3, 4);41int sum→ 10 = reduceInts(nums[1, 2, 3, 4], 0, (a, b) -> a + b);42int product = reduceInts(nums[1, 2, 3, 4], 1, (a, b) -> a * b);43System.out.println("sum = " + sum);
  16. acc ← 1

    pass 2 of 2
    24private static int reduceInts(List<Integer> items[1, 2, 3, 4], int identity1, IntBinaryOperator op⟨PassBehavior lambda D⟩) {25    int acc→ 1 = identity;26    for (int v : items) {
  17. return acc;

    28    }29    return acc24;30}
  18. product ← 24

    41int sum = reduceInts(nums, 0, (a, b) -> a + b);42int product→ 24 = reduceInts(nums[1, 2, 3, 4], 1, (a, b) -> a * b);43System.out.println("sum = " + sum10);44System.out.println("product = " + product24);
    outputsum = 10
    product = 24
  1. names ← [Alice, Bob, Charlie]

    32public static void main(String[] args) {33    List<String> names→ [Alice, Bob, Charlie] = Arrays.asList("Alice", "Bob", "Charlie");3435    System.out.println("lengths: " + map(names[Alice, Bob, Charlie], s -> s.length()));
  2. out ← []

    6private static <T, R> List<R> map(List<T> items[Alice, Bob, Charlie], Function<T, R> fn⟨PassBehavior lambda A⟩) {7    List<R> out→ [] = new ArrayList<>();8    for (T item : items) {
  3. for (T item : items)

    pass 1 of 3
    7List<R> out = new ArrayList<>();8for (T itemAlice : items[Alice, Bob, Charlie]) {9    out.add(fn.apply(itemAlice));10}
    All 3 passes — pass 1 is the card above
    passitem
    1Alice
    2Bob
    3Charlie
  4. return out;

    10    }11    return out[5, 3, 7];12}
  5. minLength ← 3

    35System.out.println("lengths: " + map(names[Alice, Bob, Charlie], s -> s.length()));3637int minLength→ 3 = 3;38System.out.println("long names: " + filter(names[Alice, Bob, Charlie], s -> s.length() >= minLength));
    outputlengths: [5, 3, 7]
  6. out ← []

    14private static <T> List<T> filter(List<T> items[Alice, Bob, Charlie], Predicate<T> pred⟨PassBehavior lambda B⟩) {15    List<T> out→ [] = new ArrayList<>();16    for (T item : items) {
  7. for (T item : items)

    pass 1 of 3
    15List<T> out = new ArrayList<>();16for (T itemAlice : items[Alice, Bob, Charlie]) {17    if (pred.test(item)) {
    All 3 passes — pass 1 is the card above
    passitem
    1Alice
    2Bob
    3Charlie
  8. if (pred.test(item))

    pass 1 of 3
    16for (T item : items) {17    if (pred.test(itemAlice)) {18        out.add(itemAlice);19    }
    All 3 passes — pass 1 is the card above
    passitem
    1Alice
    2Bob
    3Charlie
  9. return out;

    20    }21    return out[Alice, Bob, Charlie];22}
  10. nums ← [1, 2, 3, 4]

    37int minLength = 3;38System.out.println("long names: " + filter(names[Alice, Bob, Charlie], s -> s.length() >= minLength));3940List<Integer> nums→ [1, 2, 3, 4] = Arrays.asList(1, 2, 3, 4);41int sum = reduceInts(nums[1, 2, 3, 4], 0, (a, b) -> a + b);42int product = reduceInts(nums, 1, (a, b) -> a * b);
    outputlong names: [Alice, Bob, Charlie]
  11. acc ← 0

    pass 1 of 2
    24private static int reduceInts(List<Integer> items[1, 2, 3, 4], int identity0, IntBinaryOperator op⟨PassBehavior lambda C⟩) {25    int acc→ 0 = identity;26    for (int v : items) {
  12. acc ← 1

    pass 1 of 8
    25int acc = identity;26for (int v1 : items[1, 2, 3, 4]) {27    acc→ 1 = op.applyAsInt(acc, v1);28}
    All 8 passes — pass 1 is the card above
    passvacc
    110 1
    221 3
    333 6
    446 10
    511
    621 2
    732 6
    846 24
  13. return acc;

    28    }29    return acc10;30}
  14. sum ← 10

    40List<Integer> nums = Arrays.asList(1, 2, 3, 4);41int sum→ 10 = reduceInts(nums[1, 2, 3, 4], 0, (a, b) -> a + b);42int product = reduceInts(nums[1, 2, 3, 4], 1, (a, b) -> a * b);43System.out.println("sum = " + sum);
  15. acc ← 1

    pass 2 of 2
    24private static int reduceInts(List<Integer> items[1, 2, 3, 4], int identity1, IntBinaryOperator op⟨PassBehavior lambda D⟩) {25    int acc→ 1 = identity;26    for (int v : items) {
  16. return acc;

    28    }29    return acc24;30}
  17. product ← 24

    41int sum = reduceInts(nums, 0, (a, b) -> a + b);42int product→ 24 = reduceInts(nums[1, 2, 3, 4], 1, (a, b) -> a * b);43System.out.println("sum = " + sum10);44System.out.println("product = " + product24);
    outputsum = 10
    product = 24
  1. names ← [Alice, Bob, Charlie]

    32public static void main(String[] args) {33    List<String> names→ [Alice, Bob, Charlie] = Arrays.asList("Alice", "Bob", "Charlie");3435    System.out.println("lengths: " + map(names[Alice, Bob, Charlie], s -> s.length()));
  2. out ← []

    6private static <T, R> List<R> map(List<T> items[Alice, Bob, Charlie], Function<T, R> fn⟨PassBehavior lambda A⟩) {7    List<R> out→ [] = new ArrayList<>();8    for (T item : items) {
  3. for (T item : items)

    pass 1 of 3
    7List<R> out = new ArrayList<>();8for (T itemAlice : items[Alice, Bob, Charlie]) {9    out.add(fn.apply(itemAlice));10}
    All 3 passes — pass 1 is the card above
    passitem
    1Alice
    2Bob
    3Charlie
  4. return out;

    10    }11    return out[5, 3, 7];12}
  5. minLength ← 7

    35System.out.println("lengths: " + map(names[Alice, Bob, Charlie], s -> s.length()));3637int minLength→ 7 = 7;38System.out.println("long names: " + filter(names[Alice, Bob, Charlie], s -> s.length() >= minLength));
    outputlengths: [5, 3, 7]
  6. out ← []

    14private static <T> List<T> filter(List<T> items[Alice, Bob, Charlie], Predicate<T> pred⟨PassBehavior lambda B⟩) {15    List<T> out→ [] = new ArrayList<>();16    for (T item : items) {
  7. for (T item : items)

    pass 1 of 3
    15List<T> out = new ArrayList<>();16for (T itemAlice : items[Alice, Bob, Charlie]) {17    if (pred.test(item)) {
    All 3 passes — pass 1 is the card above
    passitem
    1Alice
    2Bob
    3Charlie
  8. if (pred.test(item))

    16for (T item : items) {17    if (pred.test(itemCharlie)) {18        out.add(itemCharlie);19    }
  9. return out;

    20    }21    return out[Charlie];22}
  10. nums ← [1, 2, 3, 4]

    37int minLength = 7;38System.out.println("long names: " + filter(names[Alice, Bob, Charlie], s -> s.length() >= minLength));3940List<Integer> nums→ [1, 2, 3, 4] = Arrays.asList(1, 2, 3, 4);41int sum = reduceInts(nums[1, 2, 3, 4], 0, (a, b) -> a + b);42int product = reduceInts(nums, 1, (a, b) -> a * b);
    outputlong names: [Charlie]
  11. acc ← 0

    pass 1 of 2
    24private static int reduceInts(List<Integer> items[1, 2, 3, 4], int identity0, IntBinaryOperator op⟨PassBehavior lambda C⟩) {25    int acc→ 0 = identity;26    for (int v : items) {
  12. acc ← 1

    pass 1 of 8
    25int acc = identity;26for (int v1 : items[1, 2, 3, 4]) {27    acc→ 1 = op.applyAsInt(acc, v1);28}
    All 8 passes — pass 1 is the card above
    passvacc
    110 1
    221 3
    333 6
    446 10
    511
    621 2
    732 6
    846 24
  13. return acc;

    28    }29    return acc10;30}
  14. sum ← 10

    40List<Integer> nums = Arrays.asList(1, 2, 3, 4);41int sum→ 10 = reduceInts(nums[1, 2, 3, 4], 0, (a, b) -> a + b);42int product = reduceInts(nums[1, 2, 3, 4], 1, (a, b) -> a * b);43System.out.println("sum = " + sum);
  15. acc ← 1

    pass 2 of 2
    24private static int reduceInts(List<Integer> items[1, 2, 3, 4], int identity1, IntBinaryOperator op⟨PassBehavior lambda D⟩) {25    int acc→ 1 = identity;26    for (int v : items) {
  16. return acc;

    28    }29    return acc24;30}
  17. product ← 24

    41int sum = reduceInts(nums, 0, (a, b) -> a + b);42int product→ 24 = reduceInts(nums[1, 2, 3, 4], 1, (a, b) -> a * b);43System.out.println("sum = " + sum10);44System.out.println("product = " + product24);
    outputsum = 10
    product = 24

Accept Predicate<T> to let caller define filtering logic.

Exercise: Practical.java

Build a data processor using functional interfaces