Skip to content

Lab: pluggable browse rules

☕ Java 17 · The One-Method Contract Behind Every Lambda · 3/3

Goal: write one browse() method and give it any rule as a lambda, including a rule built by a default method.

Setup: in an empty folder, create ContentType.java, Content.java, ContentRule.java, StreamingPlatform.java and Main.java. No package.

Step 1 — The content

public enum ContentType {
    MOVIE, SERIES, DOCUMENTARY
}
public record Content(String title, ContentType type, double rating) { }

The record gives us title(), type() and rating().

Step 2 — The rule

@FunctionalInterface
public interface ContentRule {
    boolean matches(Content c);
    default ContentRule not() { return c -> !matches(c); }
}

matches() is the single abstract method. not() has a body, so it doesn't count: it returns a new rule that flips the answer.

Try adding boolean isPremium(Content c); under matches(): the compiler rejects @FunctionalInterface right away. Remove the line.

Step 3 — One browse method

import java.util.ArrayList;
import java.util.List;

public class StreamingPlatform {
    private final List<Content> catalog = new ArrayList<>();

    public void add(Content c) {
        catalog.add(c);
    }

    public List<Content> browse(ContentRule rule) {
        List<Content> result = new ArrayList<>();
        for (Content c : catalog) {
            if (rule.matches(c)) {
                result.add(c);
            }
        }
        return result;
    }
}

browse() doesn't know what it filters for: the decision is plugged in from outside.

Step 4 — Three rules

import java.util.List;

public class Main {
    public static void main(String[] args) {
        StreamingPlatform platform = new StreamingPlatform();
        platform.add(new Content("Inception", ContentType.MOVIE, 8.8));
        platform.add(new Content("Stranger Things", ContentType.SERIES, 8.7));
        platform.add(new Content("Red Notice", ContentType.MOVIE, 6.4));
        platform.add(new Content("Our Planet", ContentType.DOCUMENTARY, 9.3));
        platform.add(new Content("Emily in Paris", ContentType.SERIES, 6.9));

        show("Movies only", platform.browse(c -> c.type() == ContentType.MOVIE));
        show("Top rated (8.0+)", platform.browse(c -> c.rating() >= 8.0));

        ContentRule moviesOnly = c -> c.type() == ContentType.MOVIE;
        show("Hiding movies", platform.browse(moviesOnly.not()));
    }

    private static void show(String label, List<Content> list) {
        System.out.println("=== " + label + " ===");
        for (Content c : list) {
            System.out.println("  " + c.title() + " (" + c.type() + ", " + c.rating() + ")");
        }
        System.out.println();
    }
}

The third rule isn't written by hand: it asks the existing rule for its opposite.

Step 5 — Run

$ javac *.java
$ java Main
=== Movies only ===
  Inception (MOVIE, 8.8)
  Red Notice (MOVIE, 6.4)

=== Top rated (8.0+) ===
  Inception (MOVIE, 8.8)
  Stranger Things (SERIES, 8.7)
  Our Planet (DOCUMENTARY, 9.3)

=== Hiding movies ===
  Stranger Things (SERIES, 8.7)
  Our Planet (DOCUMENTARY, 9.3)
  Emily in Paris (SERIES, 6.9)

Three different results; browse() didn't change by a single character.

Full code on GitHub

← @FunctionalInterface and Object methods · Back to the series


🎓 Preparing the OCP Java 17 exam? Exam traps and practice questions for this section → (optional)