The Use of Lambda Expressions in Java Development

1. So, what the heck is a Lambda expression?

To put it simply, a Lambda expression is like a “laziness booster” that Java 8 gifted us hardworking developers. It lets you write cleaner, more elegant code. 

In plain terms: it kicks out those long-winded, smelly anonymous inner classes and gives your code-base a fresh, minimalist vibe.

Especially when you’re wrangling collections like `List`, `Map`, and the like—once you bring in a Lambda, your code shrinks in half and suddenly feels way more readable.

Here's an example without Lambda (Traditional Java approach) : 

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Print names in uppercase (before Java 8)
for (String name : names) {
    System.out.println(name.toUpperCase());
}

Here's an example with Lambda (Java 8 or later) : 

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Print names in uppercase using lambda
names.forEach(name -> System.out.println(name.toUpperCase()));

Back in the day, if you wanted to handle a button click, you had to either `new` up a listener or write a bunch of interfaces. Now? Just one Lambda line and done. It’s like a programmer’s dream tool for getting more done with less typing.

But you can’t just go slapping Lambdas everywhere. They need a trusty sidekick called a functional interface

What’s that? 

Easy—just an interface with one abstract method. Not two, not three—just one. Be greedy and try to sneak in more? Java’s not having it.

Of course, you can create your own functional interfaces, but Java’s got you covered with a bunch of ready-made ones like `Runnable`, `Callable`, `Comparator`, and more. Just grab one and roll with it—super convenient!

2. Functional Interfaces

If an interface is marked with the @FunctionalInterface annotation, then it's officially a functional interface—which means it can only have one abstract method. Here's an example of what a functional interface looks like:

@FunctionalInterface
public interface MyInterface {
    void eat();
}

Even if you don’t add the @FunctionalInterface annotation, as long as the interface contains only one abstract method, it still counts as a functional interface in the eyes of Java.

public interface MyInterface {
    void eat();
}

There’s only one exception to the “only one method” rule: a functional interface can still inherit methods from the Object class—like toString(), equals(), or hashCode()—and that’s totally allowed.

@FunctionalInterface
public interface MyInteface3 {
    void eat();

    @Override
    String toString();

    @Override
    int hashCode();
}

3. Using Lambda Expressions

Usage inside regular methods

Student class:

@FunctionalInterface
public interface Student {
    void eat();
}

Test class:

public class Test {
    public static void main(String[] args) {
        Student stu = new Student() {
            // Regular method: override and use
            @Override
            public void eat() {
                System.out.println("I am a student");
            }
        };
        stu.eat();

        // Lambda expression version:
        // Part 1: overrides the single parameterless abstract method 'eat' in Student interface, providing implementation, so no need to write method name
        // Part 2: -> operator, fixed syntax
        // Part 3: {implementation body} provides the concrete implementation of the 'eat' method in Student interface
        Student stu2 = () -> {
            System.out.println("Student is eating");
        };
        stu2.eat();
    }
}
Output: 
I am a student
Student is eating
Using methods with parameters

Student class:
@FunctionalInterface
public interface Student {
    void eat(String food);
}
Test class:
public class Test {
    public static void main(String[] args) {
        // Using lambda to override the only method in the Student interface
        Student stu2 = (foodName) -> {
            System.out.println("The student is eating " + foodName);
        };
        stu2.eat("meat");  // Output: The student is eating meat
    }
}
4. Implementing Multi-threading with Lambda Expressions

Creating threads using Lambda:
public class Test {
    public static void main(String[] args) {
        // Creating a thread using a lambda expression
        Thread t = new Thread(() -> {
            System.out.println("This thread is created using lambda");
        });
        t.start();
    }
}
5. Performing Operations with Lambda Expressions

Using Lambda expressions for operations can greatly reduce the amount of code.
Functional interface:
@FunctionalInterface
public interface Calculator<T> {
    T operation(T v1, T v2);
}
Test Class:
public class Test {
    // Calculation method
    public static Integer operator(Integer v1, Integer v2, Calculator<Integer> calculator) {
        return calculator.operation(v1, v2);
    }

    public static void main(String[] args) {
        // Using a lambda expression:
        // This means passing in two parameters and returning the result
        int add = Test.operator(5, 10, (x, y) -> {
            return x + y;
        });

        // Simplified version: much shorter and cleaner than above
        int num1 = Test.operator(6, 12, (x, y) -> x + y);
        int num2 = Test.operator(12, 6, (x, y) -> x - y);

        System.out.println(add);
        System.out.println(num1);
        System.out.println(num2);
    }
}
Output: 
18 、18 、6

6. Lambda Method References

Sometimes, we don’t necessarily need to override the method of an interface with a new implementation.

If there’s already an existing method that fits the purpose, we can use method references to reuse it for the functional interface’s implementation.

The benefit is code reuse. For example, with a functional interface:
public interface ResultOneParam {
    int method(int a);
}
Test Class:
public class Test {

    // An instance method that adds 10 to the input
    public int addTo(int a) {
        return a + 10;
    }

    // A static method that also adds 10 to the input
    public static int addTo2(int a) {
        return a + 12;
    }

    public static void main(String[] args) {
        // Lambda expression that overrides the method in the interface
        ResultOneParam lambda1 = (a) -> a + 12;

        // Method reference: uses the existing static method addTo2 in Test class 
        // as the implementation of the method in the interface
        ResultOneParam lambda2 = Test::addTo2;
        int result1 = lambda2.method(9);
        System.out.println(result1);

        // Method reference :: reuses an existing method instead of writing a new one
        Test test = new Test();
        ResultOneParam lambda3 = test::addTo;
        int result2 = lambda3.method(9);
        System.out.println(result2);
    }
}

7. Using Lambda with Collections

Lambda expressions are also very convenient for operating on collections.
They allow you to write much less code when iterating or processing data in collections.

public class Test {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(6, 10, 1, 4, 5, 0, 7, 5, 0, 1, 5);

        // Using a lambda expression to iterate over the list
        // This overrides the accept method of the Consumer interface
        list.forEach((element) -> {
            System.out.println(element);
        });

        // Simplified version:
        list.forEach(element -> System.out.println(element));

        // Lambda expression with method reference to print elements in the list
        list.forEach(System.out::print);

        // Print only even numbers in the list:
        list.forEach(element -> {
            if (element % 2 == 0) {
                System.out.println(element);
            }
        });
    }
}

Comments

Popular posts from this blog

Why Do Remote Java Transmission Objects Need to Be Serialized?

Usage of MD5 Encryption and Decryption Technology in Java Application Development

For storing mobile phone numbers of 3 billion global users, should the data type be int, string, varchar, or char? And why?