Lambdas

Lambda expressions are a new and important feature introduced in Java 8. They allow for clear and concise ways to represent one-method interface using an expression. Lambdas are a form of anonymous function in Java.

Basic Syntax:

The basic syntax of a lambda is:

(parameter1, parameter2, ...) -> expression
(parameter1, parameter2, ...) -> { statements; }

Examples

A lambda expression with no parameters.

() -> System.out.println("Hello World")

A lambda expression with one parameter.

x -> x * x

A lambda expression with multiple parameters.

(x, y) -> x + y

Functional Interfaces:

In Java, lambda expressions are represented as objects, and so they need a type. The type for a lambda is a functional interface. A functional interface is an interface that has just one abstract method (but can have multiple default or static methods).

Using Java's built-in Runnable functional interface:

Runnable run = () -> System.out.println("Running...");
new Thread(run).start();

Features:

Type Inference:

You don't always need to declare the type of a parameter. The Java compiler can infer it from the context.

// No need to declare 'String s'
Consumer<String> printer = s -> System.out.println(s);
No Return Keyword:

If the body of a lambda has a single statement, the Java runtime will return the value of that statement.

Function<Integer, String> converter = (num) -> "Number: " + num;
Multiple Statements:

If a lambda has multiple statements, they need to be enclosed in braces.

BinaryOperator<Integer> adder = (a, b) -> {
  int sum = a + b;
  return sum;
};

Benefits

Usage

with Runnable

Traditionally, if you wanted to create a new thread, you might do it with an anonymous inner class:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Traditional way of running a thread.");
    }
}).start();

With lambdas, it becomes:

new Thread(() -> System.out.println("Lambda way of running a thread.")).start();

with Comparator

Old way using anonymous inner class:

List<String> names = Arrays.asList("Mike", "Anna", "Xander", "Zoe");
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});

Using lambda:

names.sort((o1, o2) -> o1.compareTo(o2));

with Predicate

With Java's functional interfaces, you can filter data based on conditions:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
numbers.stream().filter(n -> n % 2 == 0).forEach(System.out::println);  // Prints even numbers

with Function

Transforming data:

List<String> words = Arrays.asList("hello", "world");
List<Integer> wordLengths = words.stream()
    .map(word -> word.length())
    .collect(Collectors.toList());
System.out.println(wordLengths);  // Prints [5, 5]

with Consumer

Performing an action on each element of a collection:

List<String> names = Arrays.asList("John", "Jane", "Jack");
names.forEach(name -> System.out.println(name));

in Stream Operations

Lambdas can be chained in stream operations to form a pipeline of operations:

List<String> animals = Arrays.asList("cat", "dog", "elephant", "bird", "ant");
animals.stream()
    .filter(animal -> animal.length() > 3)
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);

This filters animals with names longer than 3 letters, converts them to uppercase, sorts them, and then prints them.

in Event Handling (Swing)

If you're using Swing for GUI, lambdas can make event handlers more concise:

JButton button = new JButton("Click me!");
button.addActionListener(event -> System.out.println("Button clicked!"));

Exercises

List Filtering

Given a list of integers, filter out all the even numbers.

Task: Use lambdas and the Stream API to accomplish this.

String Sorting

Given a list of strings, sort the strings based on their length in descending order.

Task: Use lambdas and the Stream API to accomplish this.

Sum of Squares

Given a list of integers, find the sum of the squares of the numbers.

Task: Use lambdas and the Stream API to calculate the sum.

Custom Functional Interface

Define a functional interface called StringProcessor that has a method process that takes a string as a parameter and returns a string. Implement this interface using a lambda to reverse the input string.

Solutions

List Filtering

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
System.out.println(evenNumbers);  // Outputs: [2, 4, 6, 8, 10]

String Sorting

List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "fig");
List<String> sortedWords = words.stream()
    .sorted((s1, s2) -> s2.length() - s1.length())
    .collect(Collectors.toList());
System.out.println(sortedWords);  // Outputs: [banana, cherry, apple, date, fig]

Sum of Squares

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sumOfSquares = numbers.stream()
    .mapToInt(n -> n * n)
    .sum();
System.out.println(sumOfSquares);  // Outputs: 55

Custom Functional Interface

@FunctionalInterface
interface StringProcessor {
   String process(String str);
}

public class ReverseString {
public static void main(String[] args) {
StringProcessor reverser = str -> new StringBuilder(str).reverse().toString();

        String input = "lambda";
        System.out.println(reverser.process(input));  // Outputs: adbmal
    }
}