Fundamentals 13 min read

Master Java 8 Lambda Expressions: From Anonymous Classes to Functional Interfaces

This guide explains Java 8’s Lambda expressions, covering their purpose, syntax, functional interfaces, method references, and practical examples such as thread creation, event handling, collection processing, and Stream API usage, while comparing them to traditional anonymous classes and highlighting compilation differences.

Senior Brother's Insights
Senior Brother's Insights
Senior Brother's Insights
Master Java 8 Lambda Expressions: From Anonymous Classes to Functional Interfaces

Purpose of Lambda Expressions

Java 8 introduced lambda expressions to reduce boilerplate when a single‑method implementation is needed. They allow a block of code to be passed as a value, enabling functional‑style programming and simplifying APIs that accept behavior such as java.util.function interfaces.

Syntax

The general form is (parameters) -> expression or (parameters) -> { statements; }. Parameter lists may be empty, a single identifier (parentheses optional), or multiple identifiers separated by commas. Types can be omitted and inferred by the compiler.

() -> body
(param) -> body
(param) -> { body; }
(param1, param2) -> { body; }
(type1 p1, type2 p2) -> { body; }

Common Lambda Forms

Zero‑parameter: () -> "Hello" Single‑parameter printing: System.out::println or s -> System.out.print(s) Arithmetic: x -> 2 * x Two‑parameter subtraction: (x, y) -> x - y Two‑parameter sum with explicit types:

(int x, int y) -> x + y

Functional Interfaces

A functional interface declares exactly one abstract method (aside from Object methods). It can be marked with @FunctionalInterface to enforce this constraint at compile time. Common examples are java.lang.Runnable, java.util.function.Function, and java.util.function.Predicate.

@FunctionalInterface
public interface Runnable {
    void run();
}

Lambdas can be assigned directly to variables of a functional‑interface type.

Method References

Method references provide a compact syntax for lambdas that merely invoke an existing method. The forms are ClassName::methodName for static methods or constructors, and instance::methodName for instance methods.

// Constructor reference (no‑arg)
Supplier<Map<String, String>> mapSupplier = HashMap::new;

// Instance method reference with one argument
Consumer<String> printer = System.out::println;

// Comparator using a method reference
Comparator<Computer> comp = Comparator.comparing(Computer::getAge);

Practical Examples

Thread Initialization

// Anonymous class (pre‑Java 8)
new Thread(new Runnable() {
    @Override public void run() {
        System.out.println("Thread started");
    }
}).start();

// Lambda expression
new Thread(() -> System.out.println("Thread started")).start();

// Lambda with multiple statements
new Thread(() -> {
    System.out.println("Step 1");
    System.out.println("Step 2");
}).start();

Event Handling (Swing)

// Anonymous class
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("Button clicked");
    }
});

// Lambda expression
button.addActionListener(e -> System.out.println("Button clicked"));

// Lambda with multiple statements
button.addActionListener(e -> {
    System.out.println("First line");
    System.out.println("Second line");
});

List Traversal

// Traditional for‑each loop
for (String s : list) {
    System.out.println(s);
}

// Lambda with explicit parameter
list.forEach(s -> System.out.println(s));

// Method reference
list.forEach(System.out::println);

Predicate Filtering

@Test
public void testPredicate() {
    List<String> list = Arrays.asList("alpha", "beta", "gamma");
    filter(list, s -> "gamma".equals(s));
    filter(list, s -> s.length() == 5);
}

public static void filter(List<String> list, Predicate<String> condition) {
    for (String item : list) {
        if (condition.test(item)) {
            System.out.println("Matched: " + item);
        }
    }
}

// Stream‑based equivalents
list.stream().filter(condition::test).forEach(System.out::println);
list.stream().filter(condition).forEach(System.out::println);

Stream Operations

Stream.of("a", "b", "c")
      .map(String::toUpperCase)
      .forEach(System.out::println);

Differences Between Lambdas and Anonymous Classes

Keyword this : In an anonymous class, this refers to the anonymous class instance; in a lambda, it refers to the enclosing class instance.

Compilation: Lambdas are compiled to private synthetic methods and invoked via the invokedynamic instruction, producing no separate .class file. Anonymous classes generate a distinct class file.

Conclusion

Lambda expressions provide a concise way to pass behavior as data, enabling method references, stream pipelines, and predicate‑based filtering. Mastery comes from applying the patterns shown above in real code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaLambdaFunctional InterfaceStreamjava8Method Reference
Senior Brother's Insights
Written by

Senior Brother's Insights

A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.