Java • Java-Specific Topics

Lambda Expressions — Write basic lambda expressions

Learn how to write concise, inline functions that make your Java code shorter and more expressive.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 9 figures

SECTION 1

Historical Context & Motivation

For nearly two decades, Java was one of the most popular programming languages in the world — but it had a reputation for being verbose. Simple tasks like sorting a list or responding to a button click required writing entire classes just to define a tiny piece of behavior. Developers often joked that Java made you write ten lines of code to do what other languages accomplished in one. The introduction of lambda expressions in Java 8 changed all of that, bringing a powerful feature from the world of functional programming into the Java ecosystem.

The idea behind lambda expressions is not new. The concept traces its roots all the way back to the 1930s and a branch of mathematics called lambda calculus, developed by mathematician Alonzo Church. Over the decades, many programming languages adopted this concept — Lisp in the 1950s, Python in the 1990s, and JavaScript shortly after. Java developers had been requesting this feature for years before it finally arrived.

1936 • Lambda Calculus Invented

Alonzo Church introduces lambda calculus, a formal system for expressing computation using anonymous functions. This mathematical framework laid the theoretical groundwork for functional programming.

1958 • Lisp Adopts Lambdas

Lisp, one of the earliest programming languages, includes lambda expressions as a core feature, allowing programmers to pass functions as data.

1995 • Java Released Without Lambdas

Java 1.0 is released by Sun Microsystems. It uses anonymous inner classes for inline behavior, which are functional but notoriously verbose.

2014 • Java 8 Introduces Lambdas

After years of community demand, Java 8 officially adds lambda expressions, along with the Stream API and functional interfaces, modernizing the language significantly.

So what problem do lambdas solve? Before Java 8, if you wanted to pass a small piece of behavior — like a custom comparator for sorting — you had to create an entire anonymous inner class with boilerplate code. Lambda expressions let you express that same behavior in a single, readable line. The central question this lesson addresses is: How do you write a lambda expression in Java, and when should you use one?

SECTION 2

Core Principles & Definitions

Before diving into syntax, you need to understand a few foundational ideas. A lambda expression is essentially a short block of code that takes in parameters, does something, and optionally returns a value — without needing a name. Think of it as a compact, anonymous method that you can write right where you need it.

1

Anonymous Function

A lambda is a function without a name. Unlike a regular method that belongs to a class, a lambda is defined inline and used immediately or passed around as a value.
2

Functional Interface

A lambda can only be used where Java expects a functional interface — an interface with exactly one abstract method. Examples include Runnable, Comparator, and Predicate.
3

The Arrow Operator

Every lambda uses the -> (arrow) operator. The left side lists parameters, and the right side contains the body — the code that executes.
4

Type Inference

Java can often figure out the parameter types automatically from context, so you don't have to write them explicitly. This keeps lambda expressions short and clean.
5

Replaces Anonymous Classes

Lambdas replace verbose anonymous inner classes in most situations. They achieve the same result with far less code, improving readability and reducing errors.
✦

KEY TAKEAWAY

Think of a lambda expression like a sticky note with instructions. Instead of writing a full letter (an entire class) to tell someone "sort these names alphabetically," you jot a quick note and hand it over. The note has no name, no envelope — it's just the instructions themselves. That's what a lambda is: a quick, unnamed set of instructions you pass directly to the code that needs them.

SECTION 3

Anatomy of a Lambda Expression

The best way to understand lambda syntax is to see it broken apart visually. The diagram below shows the three essential components of a lambda expression and how they map to a traditional method.

Anatomy of a Lambda Expression(String name) -> name.toUpperCase()PARAMETER LISTInput values the lambda receivesARROWSeparates params → bodyBODYThe expression or code blockEquivalent Traditional Methodpublic String transform(String name) {return name.toUpperCase();}The lambda compresses the full method into a single expression
The diagram shows how a lambda expression consists of three parts: the parameter list in parentheses, the arrow operator (->), and the body. Notice how the full five-line method compresses into a single line.

As you can see, a lambda expression strips away all the ceremony of a traditional method. There's no access modifier like public, no return type declaration, and no method name. Java infers the return type from the body. When the body is a single expression, the result is automatically returned — you don't even need the return keyword.

SECTION 4

How Lambda Syntax Works

Lambda expressions in Java follow a specific set of syntax rules. Understanding these rules will help you write lambdas confidently in different situations. Let's look at the key syntax patterns.

Syntax Pattern 1: Single Parameter, Single Expression

SINGLE PARAMETER FORM

x−>x∗xx -> x * x

When there is exactly one parameter, parentheses are optional. The body is a single expression whose result is automatically returned.

Syntax Pattern 2: Multiple Parameters

MULTIPLE PARAMETER FORM

(a,b)−>a+b(a, b) -> a + b

When there are two or more parameters, parentheses are required. Types can be included — (int a, int b) -> a + b — but Java usually infers them.

Syntax Pattern 3: No Parameters

NO PARAMETER FORM

()−>System.out.println("Hello!")() -> System.out.println("Hello!")

When there are no parameters, you must write empty parentheses. This is commonly used with Runnable, which takes no arguments.

Syntax Pattern 4: Multi-Line Body

BLOCK BODY FORM

(x,y)−>intsum=x+y;returnsum;(x, y) -> { int sum = x + y; return sum; }

When the body has more than one statement, you must wrap it in curly braces and use an explicit return statement if the lambda needs to produce a value.

⚠

Common Mistake

If you use curly braces for the body, you must include a return statement for any lambda that produces a value. Writing (x) -> { x * 2; } without return will cause a compilation error. Either drop the braces or add return x * 2;.

SECTION 5

Common Functional Interfaces

A lambda expression can only appear in a context where Java expects a functional interface — an interface with exactly one abstract method. Java provides many built-in functional interfaces in the java.util.function package. The table below summarizes the most common ones you'll encounter.

InterfaceAbstract MethodPurposeLambda Example
Predicate<T>boolean test(T t)Tests a condition and returns true/falses -> s.length() > 5
Function<T,R>R apply(T t)Transforms input of type T to output of type Rn -> n * 2
Consumer<T>void accept(T t)Performs an action with the input, returns nothings -> System.out.println(s)
Supplier<T>T get()Takes no input, produces a value() -> Math.random()
Comparator<T>int compare(T a, T b)Compares two objects for ordering(a, b) -> a.length() - b.length()

Common functional interfaces used with lambda expressions

Lambda + Functional Interface FlowYOUR CODEs -> s.length() > 5FUNCTIONAL INTERFACEPredicate<String>boolean test(String t)RESULTtrue / falseBefore Lambdas (Anonymous Inner Class)Predicate<String> longWord = new Predicate<String>() {@Overridepublic boolean test(String s) {return s.length() > 5;}};After LambdasPredicate<String> longWord = s -> s.length() > 5;7 lines1 line
The top row shows how your lambda code flows through a functional interface to produce a result. Below, compare the 7-line anonymous inner class with the 1-line lambda equivalent.

The key insight here is that the lambda and the anonymous inner class do exactly the same thing. The lambda is simply a more concise way to implement the single abstract method defined by the functional interface. When you write s -> s.length() > 5, Java knows from the Predicate<String> context that s must be a String and that the body must return a boolean.

SECTION 6

Worked Example: Sorting a List with a Lambda

Let's walk through a realistic example. Suppose you have a list of student names and you want to sort them by length (shortest name first). We'll write this using a lambda expression with Collections.sort().

Sorting Strings by Length Using a Lambda

  1. 1

    Step 1 — Create the List

    We start by creating a list of names using Arrays.asList(): List<String> names = Arrays.asList("Alice", "Bo", "Christina", "Dan");
    List contains: [Alice, Bo, Christina, Dan]
  2. 2

    Step 2 — Identify the Functional Interface

    The method Collections.sort(List<T>, Comparator<T>) expects a Comparator<String> as its second argument. Comparator is a functional interface with the abstract method int compare(String a, String b). Our lambda must take two Strings and return an int.
  3. 3

    Step 3 — Write the Lambda

    We need to compare two strings by their length. Subtracting one length from another gives us the correct ordering value. The lambda expression is: (a, b) -> a.length() - b.length() If a is shorter, the result is negative (a comes first). If b is shorter, the result is positive (b comes first).
  4. 4

    Step 4 — Put It All Together

    The complete sort call looks like this: Collections.sort(names, (a, b) -> a.length() - b.length()); The lambda is passed directly as the second argument — no need to create a separate class or variable.
  5. 5

    Step 5 — Verify the Result

    After calling sort, we print the list: System.out.println(names);
    Output: [Bo, Dan, Alice, Christina] — sorted from shortest to longest name
💡

Pro Tip

In a single-expression lambda like (a, b) -> a.length() - b.length(), the result of the expression is automatically returned. You don't need to write return. This is one of the biggest readability advantages of lambdas.

SECTION 7

Lambdas vs. Anonymous Classes vs. Named Methods

Lambda expressions aren't the only way to pass behavior in Java. You could also use anonymous inner classes or define a named method. Each approach has strengths and weaknesses. The table below compares them across several important dimensions.

FeatureLambda ExpressionAnonymous Inner ClassNamed Method
VerbosityVery concise — often one lineVerbose — 5+ lines minimumModerate — needs method + class
ReadabilityExcellent for short logicCluttered with boilerplateBest for complex or reused logic
Can access 'this'Refers to enclosing classRefers to the anonymous class itselfRefers to the containing class
ReusabilityLow — typically used once inlineLow — single useHigh — can be called from anywhere
Interface required?Must be a functional interface (1 method)Can implement any interfaceNo interface needed

Comparison of three approaches for passing behavior in Java

✦

WHEN TO USE LAMBDAS

Use a lambda when you need a short, one-time piece of logic — like filtering, sorting, or handling an event. If the logic is complex (more than 3–4 lines) or needs to be reused in multiple places, consider a named method instead. Think of lambdas like text messages — great for quick communication, but for something important and reusable, you'd write a proper email (a named method).

SECTION 8

Connection to Advanced Concepts

Once you're comfortable writing basic lambdas, you'll encounter more advanced features that build on the same foundation. Two major topics await you: method references and the Stream API. Both rely heavily on lambdas and functional interfaces.

ConceptWhat You Know Now (Lambdas)What Comes Next
Syntaxs -> s.toUpperCase()Method reference: String::toUpperCase
Usage contextSingle operations like sort or filterChained operations using Streams: filter → map → collect
Data processingProcess one element at a timeProcess entire collections with parallel pipelines
CompositionStandalone lambda for one taskCombine lambdas: predicate1.and(predicate2)

How basic lambdas connect to advanced Java features

The Stream API is where lambdas truly shine. Imagine processing a list of student grades: you could filter for passing scores, convert them to letter grades, and collect the results — all in a single chain of readable method calls, each powered by a lambda. Mastering basic lambda syntax now gives you the foundation to use these powerful tools later.

ℹ

Looking Ahead

A method reference like String::toUpperCase is just a shorthand for s -> s.toUpperCase(). When your lambda simply calls an existing method, the method reference form is cleaner. You'll learn more about these in a future lesson.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

What is a functional interface, and why is it necessary for lambda expressions in Java? Give one example of a built-in functional interface.
Show answer
A functional interface is an interface that has exactly one abstract method. Lambda expressions need functional interfaces because the lambda provides the implementation of that single abstract method. Java uses the interface's method signature to determine the parameter types and return type of the lambda. An example is Predicate<T>, which has the abstract method boolean test(T t). When you write x -> x > 10 in a Predicate context, Java knows the lambda takes one argument and returns a boolean.

Question 2: BASIC CALCULATION

Write a lambda expression that takes an integer and returns its square. Assign it to a Function<Integer, Integer> variable called square. Then show how you would call it with the value 7.
Show answer
The declaration is: Function<Integer, Integer> square = x -> x * x; The lambda takes a single parameter x and returns x * x. Since it's a single expression, no curly braces or return keyword are needed. To call it with 7, use: int result = square.apply(7); This calls the apply method defined by the Function interface, and result will be 49.

Question 3: INTERMEDIATE

Given a List<String> words = Arrays.asList("cat", "elephant", "dog", "butterfly", "ant");, write code using Collections.sort() and a lambda expression to sort the list alphabetically in reverse order (Z to A). What will the resulting list look like?
Show answer
Use Collections.sort(words, (a, b) -> b.compareTo(a)); Here the lambda takes two String parameters a and b. By writing b.compareTo(a) instead of a.compareTo(b), we reverse the natural alphabetical order. The resulting list is [elephant, dog, cat, butterfly, ant]. The lambda implements the Comparator<String> functional interface.

Question 4: APPLIED

You are building a grade calculator. You have a List<Integer> scores containing test scores. Using a Predicate<Integer> lambda, write code that creates a predicate called isPassing (score ≥ 60), then use a loop to print only the passing scores from the list [85, 42, 73, 58, 91, 60].
Show answer
First, define the predicate: Predicate<Integer> isPassing = score -> score >= 60; Then loop through the list and test each score: for (int s : scores) { if (isPassing.test(s)) System.out.println(s); } This prints 85, 73, 91, and 60. The lambda encapsulates the passing condition in a reusable Predicate object. The .test() method invokes the lambda and returns true or false. This pattern separates the filtering logic from the loop logic, making the code easier to maintain.

Question 5: CRITICAL THINKING

A student writes the following lambda: Comparator<String> comp = (String a, String b) -> { a.length() - b.length(); }; This code does not compile. Identify the error, explain why it fails, and provide two different ways to fix it.
Show answer
The error is that the lambda body uses curly braces (a block body) but does not include a return statement. The expression a.length() - b.length() is treated as a standalone statement, not a return value. The compiler expects the compare method to return an int, but nothing is returned. Fix 1: Add return — (a, b) -> { return a.length() - b.length(); }. Fix 2 (preferred): Remove the curly braces to make it an expression body — (a, b) -> a.length() - b.length(). The second fix is cleaner because single-expression lambdas automatically return the result. This is one of the most common lambda mistakes beginners make.

SUMMARY

Lesson Summary

Summary

A lambda expression is a concise, anonymous function in Java that uses the arrow operator (->) to separate its parameters from its body. Introduced in Java 8, lambdas replace verbose anonymous inner classes and can only be used where Java expects a functional interface — an interface with exactly one abstract method. Key syntax rules include: single-parameter lambdas can skip parentheses, single-expression bodies auto-return their result, and multi-line bodies require curly braces with an explicit return statement.

Common functional interfaces include Predicate (testing conditions), Function (transforming values), Consumer (performing actions), Supplier (producing values), and Comparator (ordering objects). Lambdas are ideal for short, one-time logic; for complex or reusable behavior, named methods are preferred. Mastering lambda syntax prepares you for method references and the Stream API, which chain lambdas together for powerful data processing.

Varsity Tutors • Java • Lambda Expressions — Write basic lambda expressions