THE MODN CHRONICLES

Interview Prep

Interview Questions Java 8 — Stream API, Lambdas, and What Interviewers Actually Test

Java 8 changed how Java is written. Indian service companies and product companies now test Java 8 features in every Java interview. Here are the questions — with code examples and the thinking process interviewers expect.

Developer working on Java 8 code during interview preparation

Java 8 is no longer optional — it is the baseline every interviewer expects you to know.

Why Java 8 Matters in Every Interview

Java 8 is no longer “new” — it is the baseline. Indian companies (TCS, Infosys, Wipro, product companies) now expect Java 8 fluency. If you write pre-Java-8 code in an interview, you look outdated. Streams, lambdas, and functional interfaces are not advanced topics anymore — they are table stakes.

The shift happened because enterprise Java projects migrated to Java 8+ and never looked back. Every Spring Boot application uses lambdas. Every data processing pipeline uses streams. Every modern API uses Optional. Interviewers test Java 8 because that is what you will write on day one of the job.

This guide covers the actual Java 8 questions asked in Indian interviews — organized by topic, with code examples and the reasoning interviewers expect. Not textbook definitions, but the practical understanding that separates candidates who use Java 8 from candidates who understand it.

If you cannot write a stream pipeline from memory in an interview, you are not ready for any Java role at any Indian company.

Stream API Questions

The Stream API is tested in 80%+ of Java 8 interviews. Interviewers do not ask you to define what a stream is — they give you a problem and expect you to solve it using a stream pipeline. Here are the three most common patterns.

Q1: Filter a list of employees with salary > 50000 and sort by name

Why they ask: This tests the most fundamental stream pipeline — filter, sorted, collect. If you cannot write this fluently, the interview is effectively over.

What the interviewer wants: Clean pipeline with proper method references where possible. They also want you to explain that streams are lazy — nothing executes until the terminal operation (collect) is called.

List<Employee> result = employees.stream()
    .filter(e -> e.getSalary() > 50000)
    .sorted(Comparator.comparing(Employee::getName))
    .collect(Collectors.toList());

// Key points:
// - filter() is an intermediate operation (lazy)
// - sorted() is a stateful intermediate operation
// - collect() is the terminal operation that triggers execution
// - Comparator.comparing() with method reference is cleaner than lambda

Q2: Find the second highest salary from a list using streams

Why they ask: This tests sorted + skip + findFirst — a pattern that shows you understand stream ordering and short-circuiting operations.

Optional<Integer> secondHighest = salaries.stream()
    .distinct()                          // Remove duplicates
    .sorted(Comparator.reverseOrder())   // Sort descending
    .skip(1)                             // Skip the highest
    .findFirst();                        // Get the second

// Handle the Optional properly
secondHighest.ifPresentOrElse(
    salary -> System.out.println("Second highest: " + salary),
    () -> System.out.println("No second highest found")
);

Follow-up trap: “What if there are duplicate highest values?” That is why distinct() matters. Without it, skip(1) might skip a duplicate of the highest, not the actual second highest value.

Q3: Group employees by department and count them

Why they ask: Collectors.groupingBy is the most powerful collector and the one most candidates struggle with. This tests whether you understand downstream collectors.

Map<String, Long> countByDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.counting()
    ));

// More complex: group by department, get average salary
Map<String, Double> avgSalaryByDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.averagingDouble(Employee::getSalary)
    ));

What separates good answers: Knowing that groupingBy takes an optional downstream collector. Being able to chain groupingBy with counting(), averagingDouble(), mapping(), or even another groupingBy for multi-level grouping.

Lambda Expressions & Method References

Lambda expressions replaced anonymous inner classes and made Java code dramatically more concise. Every interviewer expects you to write lambdas fluently and explain when to use method references instead.

Q1: What is a lambda expression? How is it different from an anonymous inner class?

Why they ask: This tests whether you understand the conceptual shift Java 8 introduced — from object-oriented callbacks to functional-style programming.

// Before Java 8: Anonymous inner class
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Java 8: Lambda expression
Runnable r2 = () -> System.out.println("Hello");

// Key differences:
// 1. Lambda does NOT create a new class file (.class)
// 2. Lambda uses invokedynamic bytecode (more efficient)
// 3. 'this' in lambda refers to enclosing class, not the lambda itself
// 4. Lambda can only implement functional interfaces (single abstract method)

The detail that impresses: Mentioning that lambdas use invokedynamic at the bytecode level, which is more efficient than anonymous inner classes that create a separate .class file for each instance.

Q2: Explain method references with examples

Why they ask: Method references are shorthand for lambdas. Interviewers test whether you know all four types and when each is appropriate.

// 1. Static method reference
Function<String, Integer> parser = Integer::parseInt;

// 2. Instance method of a particular object
String str = "hello";
Supplier<String> upper = str::toUpperCase;

// 3. Instance method of an arbitrary object of a type
Function<String, String> toUpper = String::toUpperCase;

// 4. Constructor reference
Supplier<ArrayList<String>> listFactory = ArrayList::new;

// When to use: method reference is preferred when the lambda
// simply calls an existing method with the same parameters
// Lambda: (s) -> s.toUpperCase()  →  Method ref: String::toUpperCase
Code editor showing Java 8 stream operations

Java 8 streams and lambdas are not just syntax sugar — they change how you think about data processing.

Functional Interfaces

Functional interfaces are the foundation that makes lambdas possible. Every lambda expression in Java is an implementation of a functional interface. Interviewers test whether you know the built-in ones and can create custom ones.

Q1: What are the built-in functional interfaces in Java 8?

Why they ask: These four interfaces (Predicate, Function, Consumer, Supplier) are used everywhere in the Stream API and Spring framework. Not knowing them signals you have not actually written Java 8 code.

// Predicate<T> — takes T, returns boolean (used in filter)
Predicate<String> isLong = s -> s.length() > 10;
list.stream().filter(isLong).collect(Collectors.toList());

// Function<T, R> — takes T, returns R (used in map)
Function<String, Integer> length = String::length;
list.stream().map(length).collect(Collectors.toList());

// Consumer<T> — takes T, returns void (used in forEach)
Consumer<String> printer = System.out::println;
list.forEach(printer);

// Supplier<T> — takes nothing, returns T (used in factory patterns)
Supplier<List<String>> listMaker = ArrayList::new;
List<String> newList = listMaker.get();

// BiFunction, BiPredicate, UnaryOperator, BinaryOperator
// are variations — know they exist, explain if asked

Q2: Write a custom functional interface

Why they ask: This tests whether you understand that @FunctionalInterface is optional (it is a compile-time check, not a requirement) and that a functional interface must have exactly one abstract method.

@FunctionalInterface
interface StringProcessor {
    String process(String input);

    // Default methods are allowed
    default StringProcessor andThen(StringProcessor after) {
        return input -> after.process(this.process(input));
    }
}

// Usage with lambda
StringProcessor trimmer = String::trim;
StringProcessor upper = String::toUpperCase;
StringProcessor pipeline = trimmer.andThen(upper);

String result = pipeline.process("  hello world  ");
// Result: "HELLO WORLD"

Key point: A functional interface can have multiple default methods and static methods — only the abstract method count matters. The @FunctionalInterface annotation is a safeguard, not a requirement.

Optional & Date-Time API

Optional and the new Date-Time API are two Java 8 features that solve real production problems. Interviewers test these because misusing them (or not using them at all) causes bugs in enterprise applications.

Q1: What is Optional and why was it introduced?

Why they ask: NullPointerException is the most common exception in Java production code. Optional was introduced to make null handling explicit and force developers to think about the absence of a value.

// Bad: returns null, caller might forget to check
public Employee findById(int id) {
    return employeeMap.get(id); // Could be null!
}

// Good: returns Optional, forces caller to handle absence
public Optional<Employee> findById(int id) {
    return Optional.ofNullable(employeeMap.get(id));
}

// Using Optional properly
findById(1)
    .map(Employee::getName)
    .orElse("Unknown Employee");

// NEVER do this — defeats the purpose of Optional
if (optional.isPresent()) {
    return optional.get(); // This is just null-checking with extra steps
}

Common mistake: Using Optional.get() without checking — this throws NoSuchElementException, which is worse than NullPointerException because it is unexpected. Always use orElse(), orElseGet(), or ifPresent().

Q2: How is the new Date-Time API different from java.util.Date?

Why they ask: The old Date and Calendar classes are mutable, not thread-safe, and have confusing APIs (months start at 0). The new API fixes all of these problems.

// Old API problems:
Date date = new Date();       // Mutable — anyone can change it
date.setTime(0);              // Not thread-safe
int month = date.getMonth();  // Returns 0 for January (confusing!)

// New API (java.time package):
LocalDate today = LocalDate.now();           // Date only, no time
LocalDateTime now = LocalDateTime.now();     // Date + time, no timezone
ZonedDateTime zoned = ZonedDateTime.now();   // Date + time + timezone

// Immutable — every operation returns a new object
LocalDate tomorrow = today.plusDays(1);      // today is unchanged
LocalDate nextMonth = today.plusMonths(1);

// Thread-safe by design (immutable objects are always thread-safe)
// Clear API: Month.JANUARY, not 0

Key differences: Immutability (thread-safe by default), clear separation of concerns (LocalDate vs LocalTime vs LocalDateTime vs ZonedDateTime), and a sensible API (Month.JANUARY instead of 0). The new API is based on the Joda-Time library that Java developers had been using as a workaround for years.

How to Prepare — By Company Type

The depth of Java 8 knowledge tested varies dramatically by company type. Here is what each expects:

Service Companies (TCS, Infosys, Wipro, Cognizant)

Test the basics: write a stream pipeline, explain lambda vs anonymous class, name the four functional interfaces, explain Optional. They want to confirm you can read and write modern Java code. Preparation time: 1 week of focused practice. Write 10-15 stream operations from memory. Know the theory behind lambdas and functional interfaces. That is enough.

Product Companies (Flipkart, Razorpay, Swiggy, Zerodha)

Test advanced stream operations: complex collectors (groupingBy with downstream), parallel streams and when NOT to use them, custom functional interfaces with composition. They expect you to solve data processing problems using streams instead of loops. Preparation time: 2-3 weeks. Practice converting loop-based solutions to stream-based solutions. Understand performance implications of streams vs loops.

GCCs (Google, Amazon, Microsoft India)

Test internal implementation: how does Stream.parallel() work under the hood (ForkJoinPool), what is the spliterator, how does lazy evaluation actually work in the stream pipeline, CompletableFuture composition patterns. They want to know you understand the machinery, not just the API. Preparation time: 3-4 weeks. Read the source code of key stream operations. Understand the ForkJoinPool and work-stealing algorithm.

Practice With Real Interview Simulations

Reading Java 8 questions is not the same as answering them under pressure. Practice with timed mock interviews that test your stream pipeline writing speed and your ability to explain functional concepts clearly.

TRY INTERVIEW PRACTICE →

Java 8 is not a feature set to memorize. It is a way of thinking about code. The candidates who get offers write streams naturally — not because they memorized the syntax, but because they think in pipelines.

Java 8 fluency is now non-negotiable for any Java role in India. Service companies test the basics, product companies test practical application, and GCCs test internal understanding. The good news: Java 8 is a finite topic. Unlike DSA, which has thousands of problems, Java 8 has a clear set of features you can master in weeks. Focus on writing code, not reading theory. Every stream pipeline you write from memory in practice is one less you will struggle with in the interview.

Prepare for Your Java 8 Interview

Practice with AI-powered mock interviews, get your resume ATS-ready, and walk into your next Java interview with confidence.

Free · AI-powered · Instant feedback