Phase 7
Java 8 LTS • Functional Interfaces, Lambdas, Method References & Streams
📂 Module 2_ Java LTS Features \ 01_Java8_Demos
7.1 Single Abstract Method (SAM) Interfaces, Lambdas & Method References
📖 Cay S. Horstmann • Core Java Volume I — Chapter 6, Section 6.2 ("Lambda Expressions")
“A lambda expression is a block of code with parameters that can be passed to other methods. In Java, you can pass code around by using lambda expressions with functional interfaces—interfaces with a single abstract method. The Java compiler translates a lambda expression into a private method inside the containing class and invokes it using the invokedynamic bytecode instruction, avoiding the class generation overhead of anonymous inner classes.”
-
SAM Axiom
Single Abstract Method Requirement: Any interface with exactly one abstract method is a Functional Interface (annotated with
@FunctionalInterface). Default and static methods do not count toward this limit. -
Method Reference Syntax
Four Forms:
Class::staticMethod,instance::instanceMethod,Class::instanceMethod, and Constructor ReferencesClass::new.
💡 Real-World Analogy: Passing Code as a Recipe vs Hiring a Chef
In Java 7, if you wanted to tell a method how to sort pizzas, you had to hire an entire temporary chef (create an Anonymous Inner Class with boilerplate
In Java 8, you simply text the recipe directly:
new Comparator<Pizza>() { public int compare(...) {...} }).
In Java 8, you simply text the recipe directly:
(p1, p2) -> p1.getPrice() - p2.getPrice()! You pass pure behavior as data.
⚡ Pro-Tip / Interview Memory Hook: Lambda vs Anonymous Inner Class Memory Secret
Anonymous Inner Classes: Generate a physical
Lambdas: Do NOT generate separate class files. The HotSpot compiler uses the
Outer$1.class file on disk and allocate a distinct object on the Heap every single time!
Lambdas: Do NOT generate separate class files. The HotSpot compiler uses the
invokedynamic (INDY) bytecode opcode with LambdaMetafactory, generating lightweight function pointers that avoid heap allocation overhead!
📂 Module 2_ Java LTS Features/.../Tester1003NeedOfMethodReferenceConstructor.java
package com.accenture.ltt.mr.custom.interfaces;
class Customer {
private String name;
public Customer(String name) {
this.name = name;
}
public String getName() { return name; }
}
@FunctionalInterface
interface CustomerFactory {
Customer create(String name);
}
public class Tester1003NeedOfMethodReferenceConstructor {
public static void main(String[] args) {
// 1. Traditional Lambda syntax
CustomerFactory f1 = name -> new Customer(name);
// 2. Elegant Java 8 Constructor Method Reference: Class::new
CustomerFactory f2 = Customer::new;
Customer c = f2.create("Mohpreet Singh");
System.out.println("Created customer: " + c.getName());
}
}
👁 Execution & Memory Breakdown • Method Reference Mechanics
Line 15:
Customer::new binds directly to the single-argument constructor Customer(String) matching the SAM signature of CustomerFactory.create(String).
Runtime: No intermediate adapter class is loaded. The JVM invokes the constructor directly through the invokedynamic call site.
7.2 Java 8 Streams API: Pipelines, Lazy Evaluation & Collectors
📖 Cay S. Horstmann • Core Java Volume II — Chapter 1, Section 1.1 ("From Iterating to Stream Operations")
“Streams differ from collections in three crucial ways:
1. A stream does not store its elements; they may be stored in an underlying collection or generated on demand.
2. Stream operations do not mutate their source; they return a new stream with the results.
3. Stream operations are lazily evaluated when possible. They are not carried out until an ultimate terminal operation requests the result.”
-
Lazy Pipeline Rule
No Terminal = Zero Execution: Intermediate operations (
filter(),map(),peek()) execute NOTHING until a terminal operation (collect(),count(),forEach()) pulls elements. -
Short-Circuiting
O(1) Optimization: Operations like
findFirst(),anyMatch(), andlimit(n)terminate the pipeline immediately without traversing remaining stream elements.
💡 Real-World Analogy: The Factory Assembly Line Conveyor Belt
Imagine an automated car manufacturing line:
• Intermediate Operations (filter, map): These are robotic arms stationed along the conveyor belt (e.g., "paint blue", "reject defective parts"). If the conveyor belt is turned off, the robots do nothing!
• Terminal Operation (collect): Turning on the master switch at the shipping warehouse. Parts are pulled down the conveyor belt one by one, transformed in-flight, and packed into shipping crates!
• Intermediate Operations (filter, map): These are robotic arms stationed along the conveyor belt (e.g., "paint blue", "reject defective parts"). If the conveyor belt is turned off, the robots do nothing!
• Terminal Operation (collect): Turning on the master switch at the shipping warehouse. Parts are pulled down the conveyor belt one by one, transformed in-flight, and packed into shipping crates!
📂 Module 2_ Java LTS Features/.../Tester1001Filter.java & Tester1003Collect.java
package com.accenture.ltt.nonterminal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamMasteryDemo {
record Product(int id, String name, String category, double price) {}
public static void main(String[] args) {
List<Product> products = List.of(
new Product(101, "Laptop", "Electronics", 1200.0),
new Product(102, "Smartphone", "Electronics", 800.0),
new Product(103, "Desk Chair", "Furniture", 150.0),
new Product(104, "Coffee Maker", "Appliances", 90.0)
);
// 1. Filter + Map + Collect to List
List<String> premiumElectronics = products.stream()
.filter(p -> p.category().equals("Electronics"))
.filter(p -> p.price() > 500.0)
.map(Product::name)
.collect(Collectors.toList());
System.out.println("Premium Electronics: " + premiumElectronics);
// 2. GroupingBy Category & Calculating Average Price
Map<String, Double> avgPricePerCategory = products.stream()
.collect(Collectors.groupingBy(
Product::category,
Collectors.averagingDouble(Product::price)
));
System.out.println("Average Prices: " + avgPricePerCategory);
}
}
👁 Execution & Memory Breakdown • Stream Lazy Evaluation
Step 1 (Spliterator Pipeline):
products.stream() creates a Spliterator over the source array without copying elements into a new buffer.
Step 2 (Fusing Stages): The HotSpot JIT compiler fuses the two
filter() predicates and the map() transformation into a single unified loop pass, eliminating intermediate heap collections.