Java Advanced OOP Inner Classes Tutorial
Advanced Topic

Java Inner Classes - Complete Tutorial

Master Java Inner Classes: Learn nested classes, inner classes, static nested classes, local classes, anonymous classes, lambda expressions, and real-world applications.

Nested Classes

Class within a class

Inner Classes

Non-static nested class

Anonymous Classes

Class without name

Lambda Expressions

Java 8+ functional

1. Introduction to Inner Classes

Inner Classes (also called Nested Classes) are classes defined within another class. They allow you to logically group classes that are only used in one place, increase encapsulation, and create more readable and maintainable code.

Types of Inner Classes
  • Inner Class (Non-static): Associated with outer class instance
  • Static Nested Class: Static member of outer class
  • Local Class: Defined within a method or block
  • Anonymous Class: Class without a name
  • Lambda Expressions: Functional interface implementation (Java 8+)
Common Use Cases
  • Event Listeners: GUI event handling
  • Callback Mechanisms: Asynchronous programming
  • Iterator Patterns: Custom collection iterators
  • Builder Pattern: Object construction
  • Data Hiding: Implementation details
  • Adapter Classes: Interface adaptation
Inner Classes Hierarchy Visualization
Outer Class
Inner Class
(Non-static)
Static Nested Class
Local Class
Anonymous Class
Lambda Expression
InnerClassesDemo.java
// Outer Class
public class InnerClassesDemo {
    
    // Instance variable of outer class
    private String outerMessage = "Hello from Outer Class";
    private static String staticOuterMessage = "Static message from Outer Class";
    
    // === 1. INNER CLASS (Non-static nested class) ===
    class InnerClass {
        private String innerMessage = "Hello from Inner Class";
        
        // Inner class can access outer class members (including private)
        public void display() {
            System.out.println("Inner Class accessing:");
            System.out.println("  - Outer instance variable: " + outerMessage);
            System.out.println("  - Outer static variable: " + staticOuterMessage);
            System.out.println("  - Inner instance variable: " + innerMessage);
        }
        
        // Inner class can have its own methods
        public void innerMethod() {
            System.out.println("Inner class method called");
        }
    }
    
    // === 2. STATIC NESTED CLASS ===
    static class StaticNestedClass {
        private String nestedMessage = "Hello from Static Nested Class";
        
        public void display() {
            System.out.println("Static Nested Class accessing:");
            System.out.println("  - Outer static variable: " + staticOuterMessage);
            System.out.println("  - Nested instance variable: " + nestedMessage);
            // Cannot access non-static outer members directly
            // System.out.println("  - Outer instance variable: " + outerMessage); // ERROR
        }
    }
    
    // === 3. LOCAL CLASS (defined inside a method) ===
    public void demonstrateLocalClass() {
        // Local variable (must be effectively final to be accessed by local class)
        final String localVar = "Local variable in method";
        String effectivelyFinalVar = "Effectively final variable";
        
        // Local class definition inside method
        class LocalClass {
            private String localClassMessage = "Hello from Local Class";
            
            public void display() {
                System.out.println("Local Class accessing:");
                System.out.println("  - Outer instance variable: " + outerMessage);
                System.out.println("  - Outer static variable: " + staticOuterMessage);
                System.out.println("  - Local variable: " + localVar);
                System.out.println("  - Effectively final variable: " + effectivelyFinalVar);
                System.out.println("  - Local class variable: " + localClassMessage);
            }
        }
        
        // Create instance of local class and use it
        LocalClass local = new LocalClass();
        local.display();
    }
    
    // === 4. ANONYMOUS CLASS ===
    interface Greeting {
        void sayHello();
        void sayGoodbye();
    }
    
    public void demonstrateAnonymousClass() {
        // Anonymous class implementing Greeting interface
        Greeting anonymousGreeting = new Greeting() {
            private String greeting = "Hello from Anonymous Class";
            
            @Override
            public void sayHello() {
                System.out.println(greeting);
                System.out.println("Accessing outer variable: " + outerMessage);
            }
            
            @Override
            public void sayGoodbye() {
                System.out.println("Goodbye from Anonymous Class");
            }
            
            // Anonymous class can have additional methods (but can't call them from outside)
            public void extraMethod() {
                System.out.println("Extra method in anonymous class");
            }
        };
        
        anonymousGreeting.sayHello();
        anonymousGreeting.sayGoodbye();
        // anonymousGreeting.extraMethod(); // ERROR: Not part of Greeting interface
    }
    
    // === 5. LAMBDA EXPRESSION (Java 8+) ===
    @FunctionalInterface
    interface Calculator {
        int calculate(int a, int b);
    }
    
    public void demonstrateLambda() {
        // Lambda expression implementing Calculator interface
        Calculator adder = (a, b) -> a + b;
        Calculator multiplier = (a, b) -> a * b;
        
        System.out.println("Lambda Examples:");
        System.out.println("5 + 3 = " + adder.calculate(5, 3));
        System.out.println("5 * 3 = " + multiplier.calculate(5, 3));
        
        // Lambda with multiple statements
        Calculator complexCalculator = (a, b) -> {
            int result = a + b;
            result *= 2;
            return result;
        };
        System.out.println("(5 + 3) * 2 = " + complexCalculator.calculate(5, 3));
    }
    
    // Method to demonstrate all types
    public void demonstrateAll() {
        System.out.println("=== 1. INNER CLASS ===");
        InnerClass inner = new InnerClass();
        inner.display();
        inner.innerMethod();
        
        System.out.println("\n=== 2. STATIC NESTED CLASS ===");
        StaticNestedClass staticNested = new StaticNestedClass();
        staticNested.display();
        
        System.out.println("\n=== 3. LOCAL CLASS ===");
        demonstrateLocalClass();
        
        System.out.println("\n=== 4. ANONYMOUS CLASS ===");
        demonstrateAnonymousClass();
        
        System.out.println("\n=== 5. LAMBDA EXPRESSION ===");
        demonstrateLambda();
    }
    
    public static void main(String[] args) {
        InnerClassesDemo demo = new InnerClassesDemo();
        demo.demonstrateAll();
        
        System.out.println("\n=== Creating Inner Class Instance ===");
        // To create inner class instance, need outer class instance
        InnerClassesDemo.InnerClass inner = demo.new InnerClass();
        inner.display();
        
        System.out.println("\n=== Creating Static Nested Class Instance ===");
        // Static nested class can be created without outer class instance
        InnerClassesDemo.StaticNestedClass staticNested = new InnerClassesDemo.StaticNestedClass();
        staticNested.display();
        
        System.out.println("\n=== Key Differences ===");
        System.out.println("Inner Class:");
        System.out.println("- Associated with outer class instance");
        System.out.println("- Can access outer instance members");
        System.out.println("- Created with: outerInstance.new InnerClass()");
        
        System.out.println("\nStatic Nested Class:");
        System.out.println("- Not associated with outer instance");
        System.out.println("- Can only access outer static members");
        System.out.println("- Created with: new OuterClass.StaticNestedClass()");
    }
}
Key Insight: Inner classes have access to all members of the outer class (including private members), which makes them perfect for implementing helper classes that need intimate access to the outer class's implementation. This increases encapsulation while reducing namespace pollution.

2. Inner Classes vs Static Nested Classes

Understanding the difference between inner classes (non-static) and static nested classes is crucial for proper usage. Each has distinct characteristics and use cases.

Inner Class (Non-static)
  • Instance association: Tied to outer class instance
  • Access: Can access all outer instance members
  • Memory: Has implicit reference to outer instance
  • Creation: Requires outer instance: outer.new Inner()
  • Use when: Need access to outer instance state
  • Example: Iterator in collection class
Static Nested Class
  • No instance association: Independent of outer instance
  • Access: Can only access outer static members
  • Memory: No reference to outer instance
  • Creation: Independent: new Outer.StaticNested()
  • Use when: Helper class logically related to outer
  • Example: Builder pattern, utility classes

Complete Comparison Table

Aspect Inner Class (Non-static) Static Nested Class
Association Tied to outer class instance Independent of outer instance
Memory Overhead Has implicit reference to outer (extra 4-8 bytes) No reference to outer instance
Access to Outer All outer members (including private) Only static members of outer
Instantiation outerInstance.new Inner() new Outer.StaticNested()
Static Members Cannot have static members (except final constants) Can have static members
Can be Abstract Yes Yes
Can be Final Yes Yes
Serialization Serializes with outer instance Serializes independently
Performance Slightly slower (indirect access) Same as regular class
Use Case Intimate helper, iterator, adapter Builder, utility, independent helper
InnerVsStaticComparison.java
public class InnerVsStaticComparison {
    
    // Outer class with both instance and static members
    private String outerInstanceField = "Instance Field";
    private static String outerStaticField = "Static Field";
    private int outerCounter = 0;
    private static int staticCounter = 0;
    
    // === INNER CLASS (Non-static) ===
    class InnerClass {
        // Cannot have static declarations (except constants)
        // private static int x = 10; // COMPILE ERROR
        private static final int CONSTANT = 100; // Allowed (constant)
        
        private String innerField = "Inner Field";
        
        public InnerClass() {
            outerCounter++; // Can modify outer instance field
            staticCounter++; // Can modify outer static field
        }
        
        public void display() {
            System.out.println("=== Inner Class ===");
            System.out.println("Accessing outer instance field: " + outerInstanceField);
            System.out.println("Accessing outer static field: " + outerStaticField);
            System.out.println("Outer counter: " + outerCounter);
            System.out.println("Static counter: " + staticCounter);
            System.out.println("Inner field: " + innerField);
            System.out.println("Constant: " + CONSTANT);
            
            // Can call outer instance methods
            outerMethod();
            // Can call outer static methods
            staticOuterMethod();
        }
        
        // Inner class method accessing outer's private method
        public void accessOuterPrivate() {
            privateOuterMethod(); // Can access private outer method
        }
    }
    
    // === STATIC NESTED CLASS ===
    static class StaticNestedClass {
        // Can have static members
        private static int staticNestedCounter = 0;
        private String nestedField = "Nested Field";
        
        public StaticNestedClass() {
            staticNestedCounter++;
            staticCounter++; // Can modify outer static field
            // outerCounter++; // COMPILE ERROR: Cannot access instance field
        }
        
        public void display() {
            System.out.println("\n=== Static Nested Class ===");
            // System.out.println("Accessing outer instance field: " + outerInstanceField); // ERROR
            System.out.println("Accessing outer static field: " + outerStaticField);
            System.out.println("Static counter: " + staticCounter);
            System.out.println("Static nested counter: " + staticNestedCounter);
            System.out.println("Nested field: " + nestedField);
            
            // staticOuterMethod(); // Can call outer static methods
            // outerMethod(); // ERROR: Cannot call instance methods
            
            // Need outer instance to access instance members
            InnerVsStaticComparison outer = new InnerVsStaticComparison();
            System.out.println("Via outer instance: " + outer.outerInstanceField);
            outer.outerMethod();
        }
        
        // Static method in static nested class
        public static void staticNestedMethod() {
            System.out.println("Static method in static nested class");
            System.out.println("Outer static field: " + outerStaticField);
        }
    }
    
    // Outer class methods
    public void outerMethod() {
        System.out.println("Outer instance method called");
    }
    
    private void privateOuterMethod() {
        System.out.println("Private outer method called from inner class");
    }
    
    public static void staticOuterMethod() {
        System.out.println("Outer static method called");
    }
    
    // Method to demonstrate memory implications
    public void demonstrateMemory() {
        System.out.println("\n=== Memory Implications ===");
        
        // Creating multiple inner class instances
        System.out.println("Creating 5 inner class instances:");
        for (int i = 0; i < 5; i++) {
            InnerClass inner = new InnerClass();
            inner.display();
        }
        
        System.out.println("\nCreating 5 static nested class instances:");
        for (int i = 0; i < 5; i++) {
            StaticNestedClass nested = new StaticNestedClass();
            nested.display();
        }
        
        System.out.println("\n=== Key Memory Differences ===");
        System.out.println("Inner Class:");
        System.out.println("- Each instance holds reference to outer instance");
        System.out.println("- Cannot exist without outer instance");
        System.out.println("- Can cause memory leaks if outer instance is held");
        
        System.out.println("\nStatic Nested Class:");
        System.out.println("- No reference to outer instance");
        System.out.println("- Can exist independently");
        System.out.println("- More memory efficient");
    }
    
    // Real-world example: Collection with Iterator
    class SimpleCollection {
        private String[] items;
        private int size;
        
        public SimpleCollection(int capacity) {
            items = new String[capacity];
            size = 0;
        }
        
        public void add(String item) {
            if (size < items.length) {
                items[size++] = item;
            }
        }
        
        // Inner class for iterator (needs access to collection's internal array)
        class Iterator {
            private int currentIndex = 0;
            
            public boolean hasNext() {
                return currentIndex < size;
            }
            
            public String next() {
                if (hasNext()) {
                    return items[currentIndex++];
                }
                throw new java.util.NoSuchElementException();
            }
            
            public void remove() {
                // Implementation needs access to items array
                // This is why iterator is often an inner class
            }
        }
        
        public Iterator iterator() {
            return new Iterator();
        }
    }
    
    // Real-world example: Builder pattern with static nested class
    static class Computer {
        private String CPU;
        private String RAM;
        private String storage;
        private String GPU;
        
        private Computer(Builder builder) {
            this.CPU = builder.CPU;
            this.RAM = builder.RAM;
            this.storage = builder.storage;
            this.GPU = builder.GPU;
        }
        
        // Static nested class for Builder
        static class Builder {
            private String CPU;
            private String RAM;
            private String storage;
            private String GPU;
            
            public Builder setCPU(String CPU) {
                this.CPU = CPU;
                return this;
            }
            
            public Builder setRAM(String RAM) {
                this.RAM = RAM;
                return this;
            }
            
            public Builder setStorage(String storage) {
                this.storage = storage;
                return this;
            }
            
            public Builder setGPU(String GPU) {
                this.GPU = GPU;
                return this;
            }
            
            public Computer build() {
                return new Computer(this);
            }
        }
        
        @Override
        public String toString() {
            return "Computer [CPU=" + CPU + ", RAM=" + RAM + 
                   ", Storage=" + storage + ", GPU=" + GPU + "]";
        }
    }
    
    public static void main(String[] args) {
        InnerVsStaticComparison demo = new InnerVsStaticComparison();
        
        System.out.println("=== Creating and Using Inner Class ===");
        // Inner class requires outer instance
        InnerVsStaticComparison.InnerClass inner = demo.new InnerClass();
        inner.display();
        inner.accessOuterPrivate();
        
        System.out.println("\n=== Creating and Using Static Nested Class ===");
        // Static nested class doesn't need outer instance
        InnerVsStaticComparison.StaticNestedClass staticNested = 
            new InnerVsStaticComparison.StaticNestedClass();
        staticNested.display();
        StaticNestedClass.staticNestedMethod();
        
        // Demonstrate memory implications
        demo.demonstrateMemory();
        
        System.out.println("\n=== Real-World Examples ===");
        
        // Collection with Iterator (Inner Class)
        System.out.println("\n1. Collection with Iterator (Inner Class):");
        SimpleCollection collection = demo.new SimpleCollection(5);
        collection.add("Apple");
        collection.add("Banana");
        collection.add("Cherry");
        
        SimpleCollection.Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            System.out.println("Item: " + iterator.next());
        }
        
        // Computer Builder (Static Nested Class)
        System.out.println("\n2. Computer Builder (Static Nested Class):");
        Computer computer = new Computer.Builder()
            .setCPU("Intel i9")
            .setRAM("32GB")
            .setStorage("1TB SSD")
            .setGPU("RTX 4080")
            .build();
        System.out.println(computer);
        
        System.out.println("\n=== When to Use Which? ===");
        System.out.println("Use INNER CLASS when:");
        System.out.println("- Class needs access to outer instance state");
        System.out.println("- Implementing iterator/adapter pattern");
        System.out.println("- Class is tightly coupled to outer instance");
        System.out.println("- You need callback with access to outer state");
        
        System.out.println("\nUse STATIC NESTED CLASS when:");
        System.out.println("- Class is logically related but independent");
        System.out.println("- Implementing builder pattern");
        System.out.println("- Creating utility/helper classes");
        System.out.println("- No need to access outer instance state");
        System.out.println("- Better memory efficiency needed");
        
        System.out.println("\n=== Best Practice ===");
        System.out.println("Default to static nested class unless you need");
        System.out.println("access to outer instance members. This reduces");
        System.out.println("memory overhead and coupling.");
    }
}

3. Local & Anonymous Classes

Local classes are defined within a method or block, while anonymous classes are classes without a name, typically used for one-time implementations of interfaces or abstract classes.

LocalAnonymousClasses.java
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;

public class LocalAnonymousClasses {
    
    // === LOCAL CLASSES ===
    
    // Local class defined inside a method
    public void demonstrateLocalClass() {
        System.out.println("=== LOCAL CLASS DEMONSTRATION ===\n");
        
        // Local variables that will be accessed by local class
        final String finalLocalVar = "Final Local Variable";
        String effectivelyFinalVar = "Effectively Final Variable";
        int counter = 0; // This cannot be modified if accessed by local class
        
        // Local class definition inside method
        class LocalGreeter {
            private String localField = "Local Class Field";
            
            public void greet(String name) {
                System.out.println("Hello " + name + " from LocalGreeter!");
                System.out.println("Accessing:");
                System.out.println("  - Local field: " + localField);
                System.out.println("  - Final local variable: " + finalLocalVar);
                System.out.println("  - Effectively final variable: " + effectivelyFinalVar);
                // System.out.println("  - Counter: " + counter); // ERROR if counter is modified
                
                // Can access outer class members
                System.out.println("  - Outer static field: " + staticOuterField);
                // Need outer instance to access instance members
            }
            
            // Local class can have static constants
            public static final int LOCAL_CONSTANT = 42;
            
            // Local class cannot have static methods (except in Java 16+)
            // public static void staticMethod() {} // ERROR in Java < 16
        }
        
        // Cannot modify effectivelyFinalVar after local class definition
        // effectivelyFinalVar = "Changed"; // ERROR
        
        // Create and use local class instance
        LocalGreeter greeter = new LocalGreeter();
        greeter.greet("Alice");
        greeter.greet("Bob");
        
        System.out.println("\nLocal constant: " + LocalGreeter.LOCAL_CONSTANT);
        
        // Local class can implement interfaces
        interface LocalInterface {
            void perform();
        }
        
        class LocalImplementation implements LocalInterface {
            @Override
            public void perform() {
                System.out.println("Local implementation performing...");
            }
        }
        
        LocalImplementation localImpl = new LocalImplementation();
        localImpl.perform();
    }
    
    // Local class in a block
    public void demonstrateLocalClassInBlock() {
        System.out.println("\n=== LOCAL CLASS IN BLOCK ===");
        
        for (int i = 0; i < 3; i++) {
            // Local class defined inside loop block
            class BlockLocal {
                private int iteration;
                
                public BlockLocal(int iteration) {
                    this.iteration = iteration;
                }
                
                public void display() {
                    System.out.println("BlockLocal iteration " + iteration);
                    // Can access loop variable i? NO - i is not effectively final
                    // System.out.println("Loop i: " + i); // ERROR
                }
            }
            
            BlockLocal blockLocal = new BlockLocal(i);
            blockLocal.display();
        }
    }
    
    // === ANONYMOUS CLASSES ===
    
    interface EventListener {
        void onEvent(String eventName);
        void onError(String errorMessage);
    }
    
    abstract class AbstractProcessor {
        public abstract void process(String data);
        public void log(String message) {
            System.out.println("Log: " + message);
        }
    }
    
    public void demonstrateAnonymousClass() {
        System.out.println("\n=== ANONYMOUS CLASS DEMONSTRATION ===\n");
        
        // 1. Anonymous class implementing interface
        EventListener anonymousListener = new EventListener() {
            private int eventCount = 0;
            
            @Override
            public void onEvent(String eventName) {
                eventCount++;
                System.out.println("Event #" + eventCount + ": " + eventName);
                System.out.println("Accessing outer static field: " + staticOuterField);
            }
            
            @Override
            public void onError(String errorMessage) {
                System.out.println("Error in anonymous listener: " + errorMessage);
            }
            
            // Additional method (not accessible from EventListener reference)
            public void extraMethod() {
                System.out.println("Extra method in anonymous class");
            }
        };
        
        anonymousListener.onEvent("Application Started");
        anonymousListener.onEvent("User Logged In");
        anonymousListener.onError("Connection lost");
        // anonymousListener.extraMethod(); // ERROR: Not in EventListener interface
        
        // 2. Anonymous class extending abstract class
        AbstractProcessor anonymousProcessor = new AbstractProcessor() {
            private String processorName = "Anonymous Processor";
            
            @Override
            public void process(String data) {
                System.out.println(processorName + " processing: " + data);
                log("Processing completed for: " + data);
            }
            
            // Can override non-abstract methods too
            @Override
            public void log(String message) {
                System.out.println("[" + processorName + "] " + message);
            }
        };
        
        anonymousProcessor.process("Sample Data");
        
        // 3. Anonymous class with constructor arguments
        // (Using a concrete class as base)
        class BaseClass {
            protected String name;
            
            public BaseClass(String name) {
                this.name = name;
            }
            
            public void display() {
                System.out.println("BaseClass: " + name);
            }
        }
        
        BaseClass extendedAnonymous = new BaseClass("Anonymous Extension") {
            private String extraInfo = "Extra Information";
            
            @Override
            public void display() {
                super.display();
                System.out.println("Additional: " + extraInfo);
            }
            
            public void newMethod() {
                System.out.println("New method in anonymous extension");
            }
        };
        
        extendedAnonymous.display();
        // extendedAnonymous.newMethod(); // ERROR: Not in BaseClass
        
        // 4. Anonymous class for one-time comparator
        List names = new ArrayList<>();
        names.add("Charlie");
        names.add("Alice");
        names.add("Bob");
        
        System.out.println("\nOriginal list: " + names);
        
        // Anonymous Comparator
        names.sort(new Comparator() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareTo(s2);
            }
        });
        
        System.out.println("Sorted list: " + names);
    }
    
    // === PRACTICAL EXAMPLE: EVENT HANDLING ===
    
    class Button {
        private String label;
        private EventListener listener;
        
        public Button(String label) {
            this.label = label;
        }
        
        public void setOnClickListener(EventListener listener) {
            this.listener = listener;
        }
        
        public void click() {
            System.out.println("\nButton '" + label + "' clicked!");
            if (listener != null) {
                listener.onEvent("ButtonClick");
            }
        }
    }
    
    public void demonstrateEventHandling() {
        System.out.println("\n=== EVENT HANDLING WITH ANONYMOUS CLASSES ===\n");
        
        Button submitButton = new Button("Submit");
        Button cancelButton = new Button("Cancel");
        
        // Anonymous class for submit button
        submitButton.setOnClickListener(new EventListener() {
            private int clickCount = 0;
            
            @Override
            public void onEvent(String eventName) {
                clickCount++;
                System.out.println("Submit button clicked " + clickCount + " times");
                System.out.println("Processing form submission...");
            }
            
            @Override
            public void onError(String errorMessage) {
                System.out.println("Submission error: " + errorMessage);
            }
        });
        
        // Anonymous class for cancel button
        cancelButton.setOnClickListener(new EventListener() {
            @Override
            public void onEvent(String eventName) {
                System.out.println("Cancelling operation...");
                System.out.println("Reset form fields...");
            }
            
            @Override
            public void onError(String errorMessage) {
                System.out.println("Cancel error: " + errorMessage);
            }
        });
        
        // Simulate button clicks
        submitButton.click();
        submitButton.click();
        cancelButton.click();
    }
    
    // === LIMITATIONS AND GOTCHAS ===
    
    public void demonstrateLimitations() {
        System.out.println("\n=== LIMITATIONS OF LOCAL/ANONYMOUS CLASSES ===\n");
        
        // 1. Cannot have static members (except constants in local classes)
        class LocalWithStatic {
            public static final int CONSTANT = 100; // OK
            // public static int variable = 200; // ERROR (in Java < 16)
            // public static void method() {} // ERROR (in Java < 16)
        }
        
        // 2. Cannot declare constructors in anonymous classes
        // (but can have instance initializer block)
        Runnable anonymousWithInit = new Runnable() {
            private String message;
            
            // Instance initializer (acts like constructor)
            {
                message = "Initialized in instance block";
                System.out.println("Anonymous class initialized: " + message);
            }
            
            @Override
            public void run() {
                System.out.println("Running: " + message);
            }
        };
        
        anonymousWithInit.run();
        
        // 3. Serialization issues
        System.out.println("\nSerialization Warning:");
        System.out.println("- Local and anonymous classes have synthetic fields");
        System.out.println("- Serialization may not work as expected");
        System.out.println("- Consider static nested classes for serialization");
        
        // 4. Memory considerations
        System.out.println("\nMemory Considerations:");
        System.out.println("- Each instance captures local variables");
        System.out.println("- Can cause memory leaks if holding references");
        System.out.println("- Anonymous classes create .class files (Class$1.class)");
    }
    
    // Outer class fields
    private String instanceField = "Instance Field";
    private static String staticOuterField = "Static Outer Field";
    
    public static void main(String[] args) {
        LocalAnonymousClasses demo = new LocalAnonymousClasses();
        
        // Demonstrate local classes
        demo.demonstrateLocalClass();
        demo.demonstrateLocalClassInBlock();
        
        // Demonstrate anonymous classes
        demo.demonstrateAnonymousClass();
        
        // Demonstrate event handling
        demo.demonstrateEventHandling();
        
        // Demonstrate limitations
        demo.demonstrateLimitations();
        
        System.out.println("\n=== WHEN TO USE LOCAL/ANONYMOUS CLASSES ===");
        System.out.println("\nUse LOCAL CLASS when:");
        System.out.println("- Need a class used only within one method");
        System.out.println("- Class needs to access method-local variables");
        System.out.println("- Want to avoid polluting outer class namespace");
        System.out.println("- Implementing a simple helper used in one place");
        
        System.out.println("\nUse ANONYMOUS CLASS when:");
        System.out.println("- Need one-time implementation of interface/abstract class");
        System.out.println("- Implementing event listeners/callbacks");
        System.out.println("- Creating comparators, runnables, etc.");
        System.out.println("- Quick implementation without creating named class");
        
        System.out.println("\n=== BEST PRACTICES ===");
        System.out.println("1. Keep local/anonymous classes small and simple");
        System.out.println("2. Use lambda expressions instead of anonymous classes when possible");
        System.out.println("3. Avoid modifying captured variables");
        System.out.println("4. Consider moving complex logic to named static nested classes");
        System.out.println("5. Be mindful of serialization requirements");
    }
}
AnonymousClassEvolution.java
// Evolution from Anonymous Classes to Lambda Expressions
import java.util.*;
import java.util.function.*;

public class AnonymousClassEvolution {
    
    // === JAVA 7 AND BEFORE: ANONYMOUS CLASSES ===
    
    interface OldStyleOperation {
        int execute(int a, int b);
    }
    
    public void java7Style() {
        System.out.println("=== JAVA 7 STYLE (Anonymous Classes) ===\n");
        
        // Anonymous class for addition
        OldStyleOperation adder = new OldStyleOperation() {
            @Override
            public int execute(int a, int b) {
                return a + b;
            }
        };
        
        // Anonymous class for multiplication
        OldStyleOperation multiplier = new OldStyleOperation() {
            @Override
            public int execute(int a, int b) {
                return a * b;
            }
        };
        
        // Anonymous class with state
        OldStyleOperation counter = new OldStyleOperation() {
            private int operationCount = 0;
            
            @Override
            public int execute(int a, int b) {
                operationCount++;
                System.out.println("Operation count: " + operationCount);
                return a - b;
            }
        };
        
        System.out.println("5 + 3 = " + adder.execute(5, 3));
        System.out.println("5 * 3 = " + multiplier.execute(5, 3));
        System.out.println("5 - 3 = " + counter.execute(5, 3));
        System.out.println("10 - 4 = " + counter.execute(10, 4));
        
        // Thread with anonymous Runnable
        Thread oldThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread running (Java 7 style)");
            }
        });
        oldThread.start();
        
        // Comparator with anonymous class
        List names = Arrays.asList("Charlie", "Alice", "Bob");
        Collections.sort(names, new Comparator() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareTo(s2);
            }
        });
        System.out.println("Sorted: " + names);
    }
    
    // === JAVA 8: LAMBDA EXPRESSIONS ===
    
    @FunctionalInterface
    interface Operation {
        int apply(int a, int b);
    }
    
    public void java8Style() {
        System.out.println("\n=== JAVA 8 STYLE (Lambda Expressions) ===\n");
        
        // Lambda expression for addition
        Operation adder = (a, b) -> a + b;
        
        // Lambda expression for multiplication
        Operation multiplier = (a, b) -> a * b;
        
        // Lambda with multiple statements
        Operation complex = (a, b) -> {
            int result = a + b;
            return result * 2;
        };
        
        System.out.println("5 + 3 = " + adder.apply(5, 3));
        System.out.println("5 * 3 = " + multiplier.apply(5, 3));
        System.out.println("(5 + 3) * 2 = " + complex.apply(5, 3));
        
        // Thread with lambda
        Thread newThread = new Thread(() -> {
            System.out.println("Thread running (Java 8 style)");
        });
        newThread.start();
        
        // Comparator with lambda
        List names = Arrays.asList("Charlie", "Alice", "Bob");
        names.sort((s1, s2) -> s1.compareTo(s2));
        System.out.println("Sorted: " + names);
        
        // Using built-in functional interfaces
        Consumer printer = s -> System.out.println("Printing: " + s);
        printer.accept("Hello Lambda!");
        
        Predicate isEven = n -> n % 2 == 0;
        System.out.println("Is 4 even? " + isEven.test(4));
        System.out.println("Is 5 even? " + isEven.test(5));
        
        // Method references
        names.forEach(System.out::println);
    }
    
    // === WHEN TO STICK WITH ANONYMOUS CLASSES ===
    
    public void whenToUseAnonymous() {
        System.out.println("\n=== WHEN TO USE ANONYMOUS CLASSES (Even in Java 8+) ===\n");
        
        // 1. When you need state (instance variables)
        Runnable statefulTask = new Runnable() {
            private int executionCount = 0;
            private String taskName = "Stateful Task";
            
            @Override
            public void run() {
                executionCount++;
                System.out.println(taskName + " executed " + executionCount + " times");
            }
        };
        
        statefulTask.run();
        statefulTask.run();
        
        // 2. When implementing interface with multiple methods
        interface MultiMethod {
            void method1();
            void method2();
            default void defaultMethod() {
                System.out.println("Default implementation");
            }
        }
        
        MultiMethod multi = new MultiMethod() {
            @Override
            public void method1() {
                System.out.println("Method 1 implemented");
            }
            
            @Override
            public void method2() {
                System.out.println("Method 2 implemented");
            }
            
            @Override
            public void defaultMethod() {
                System.out.println("Overridden default method");
            }
        };
        
        multi.method1();
        multi.method2();
        multi.defaultMethod();
        
        // 3. When extending a class (not just implementing interface)
        abstract class AbstractGreeter {
            abstract void greet();
            void sayGoodbye() {
                System.out.println("Goodbye!");
            }
        }
        
        AbstractGreeter greeter = new AbstractGreeter() {
            @Override
            void greet() {
                System.out.println("Hello from anonymous subclass!");
            }
            
            @Override
            void sayGoodbye() {
                System.out.println("Farewell!");
            }
        };
        
        greeter.greet();
        greeter.sayGoodbye();
        
        // 4. When you need to call super methods
        class Base {
            void show() {
                System.out.println("Base class show()");
            }
        }
        
        Base extended = new Base() {
            @Override
            void show() {
                super.show(); // Can call super in anonymous class
                System.out.println("Anonymous class show()");
            }
        };
        
        extended.show();
    }
    
    // === PRACTICAL EXAMPLE: EVENT SYSTEM EVOLUTION ===
    
    interface Event {
        void fire();
    }
    
    class EventSystem {
        private List listeners = new ArrayList<>();
        
        // Java 7 style: Add event with anonymous class
        public void addJava7Event(String eventName) {
            listeners.add(new Event() {
                @Override
                public void fire() {
                    System.out.println("Java 7 Event: " + eventName);
                }
            });
        }
        
        // Java 8 style: Add event with lambda
        public void addJava8Event(String eventName) {
            listeners.add(() -> System.out.println("Java 8 Event: " + eventName));
        }
        
        // Generic method with Consumer
        public void addEvent(Consumer handler, String eventName) {
            listeners.add(() -> handler.accept(eventName));
        }
        
        public void fireAll() {
            listeners.forEach(Event::fire);
        }
    }
    
    public void demonstrateEventEvolution() {
        System.out.println("\n=== EVENT SYSTEM EVOLUTION ===\n");
        
        EventSystem system = new EventSystem();
        
        // Java 7 style
        system.addJava7Event("System Startup");
        system.addJava7Event("User Login");
        
        // Java 8 style
        system.addJava8Event("Data Loaded");
        system.addJava8Event("Processing Complete");
        
        // Modern style with Consumer
        system.addEvent(name -> System.out.println("Custom: " + name), "Custom Event");
        
        system.fireAll();
    }
    
    // === PERFORMANCE COMPARISON ===
    
    public void performanceComparison() {
        System.out.println("\n=== PERFORMANCE CONSIDERATIONS ===\n");
        
        int iterations = 1000000;
        
        // Anonymous class
        long start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    // Do nothing
                }
            };
        }
        long anonymousTime = System.nanoTime() - start;
        
        // Lambda expression
        start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            Runnable r = () -> {
                // Do nothing
            };
        }
        long lambdaTime = System.nanoTime() - start;
        
        System.out.println("Creating " + iterations + " instances:");
        System.out.println("Anonymous classes: " + anonymousTime / 1000000 + " ms");
        System.out.println("Lambda expressions: " + lambdaTime / 1000000 + " ms");
        System.out.println("\nNote: Lambda expressions are generally more efficient");
        System.out.println("as they don't create new .class files and have");
        System.out.println("better runtime optimization.");
    }
    
    public static void main(String[] args) {
        AnonymousClassEvolution demo = new AnonymousClassEvolution();
        
        // Show evolution
        demo.java7Style();
        demo.java8Style();
        
        // When to still use anonymous classes
        demo.whenToUseAnonymous();
        
        // Practical example
        demo.demonstrateEventEvolution();
        
        // Performance
        demo.performanceComparison();
        
        System.out.println("\n=== EVOLUTION SUMMARY ===");
        System.out.println("\nJava 7 and before:");
        System.out.println("- Verbose anonymous classes");
        System.out.println("- Lots of boilerplate code");
        System.out.println("- Separate .class files for each anonymous class");
        
        System.out.println("\nJava 8 and later:");
        System.out.println("- Concise lambda expressions");
        System.out.println("- Functional programming support");
        System.out.println("- Method references for even more conciseness");
        System.out.println("- Better performance in most cases");
        
        System.out.println("\n=== MIGRATION GUIDELINES ===");
        System.out.println("1. Convert single-method interfaces to lambdas");
        System.out.println("2. Keep anonymous classes for:");
        System.out.println("   - Multiple method implementations");
        System.out.println("   - Classes with state (instance variables)");
        System.out.println("   - Extending classes (not just interfaces)");
        System.out.println("   - When you need to call super methods");
        System.out.println("3. Use method references where applicable");
        System.out.println("4. Test performance for critical code paths");
    }
}

4. Lambda Expressions (Java 8+)

Lambda expressions provide a clear and concise way to represent one method interface using an expression. They are a fundamental feature of functional programming in Java.

LambdaExpressionsComplete.java
import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class LambdaExpressionsComplete {
    
    // === FUNCTIONAL INTERFACES ===
    
    @FunctionalInterface
    interface StringProcessor {
        String process(String input);
        
        // Can have default methods
        default StringProcessor andThen(StringProcessor after) {
            return input -> after.process(this.process(input));
        }
        
        // Can have static methods
        static StringProcessor toUpperCase() {
            return String::toUpperCase;
        }
    }
    
    @FunctionalInterface
    interface TriFunction {
        R apply(T t, U u, V v);
    }
    
    // === LAMBDA SYNTAX VARIATIONS ===
    
    public void demonstrateSyntax() {
        System.out.println("=== LAMBDA EXPRESSION SYNTAX ===\n");
        
        // 1. No parameters
        Runnable noParam = () -> System.out.println("Hello from lambda!");
        noParam.run();
        
        // 2. Single parameter, inferred type
        Consumer singleParam = message -> System.out.println("Message: " + message);
        singleParam.accept("Hello World");
        
        // 3. Single parameter with parentheses (optional)
        Consumer singleParamWithParens = (message) -> System.out.println("Message: " + message);
        singleParamWithParens.accept("Hello Again");
        
        // 4. Multiple parameters
        BinaryOperator add = (a, b) -> a + b;
        System.out.println("5 + 3 = " + add.apply(5, 3));
        
        // 5. Multiple parameters with explicit types
        BinaryOperator multiply = (Integer a, Integer b) -> a * b;
        System.out.println("5 * 3 = " + multiply.apply(5, 3));
        
        // 6. Multiple statements in body
        BinaryOperator complex = (a, b) -> {
            int result = a + b;
            System.out.println("Calculating " + a + " + " + b);
            return result * 2;
        };
        System.out.println("(5 + 3) * 2 = " + complex.apply(5, 3));
        
        // 7. Returning a value (explicit return)
        Function converter = (num) -> {
            return "Number: " + num;
        };
        System.out.println(converter.apply(42));
        
        // 8. Returning a value (implicit return - single expression)
        Function square = num -> num * num;
        System.out.println("Square of 5: " + square.apply(5));
    }
    
    // === BUILT-IN FUNCTIONAL INTERFACES ===
    
    public void demonstrateBuiltInInterfaces() {
        System.out.println("\n=== BUILT-IN FUNCTIONAL INTERFACES ===\n");
        
        // 1. Predicate - tests a condition
        Predicate isLong = s -> s.length() > 10;
        System.out.println("Is 'Hello' long? " + isLong.test("Hello"));
        System.out.println("Is 'Hello World!' long? " + isLong.test("Hello World!"));
        
        // Predicate composition
        Predicate isEven = n -> n % 2 == 0;
        Predicate isPositive = n -> n > 0;
        Predicate isEvenAndPositive = isEven.and(isPositive);
        System.out.println("Is 4 even and positive? " + isEvenAndPositive.test(4));
        System.out.println("Is -2 even and positive? " + isEvenAndPositive.test(-2));
        
        // 2. Function - transforms input to output
        Function lengthFunction = String::length;
        System.out.println("Length of 'Hello': " + lengthFunction.apply("Hello"));
        
        // Function composition
        Function multiplyBy2 = n -> n * 2;
        Function toString = Object::toString;
        Function multiplyAndString = multiplyBy2.andThen(toString);
        System.out.println("5 * 2 as string: " + multiplyAndString.apply(5));
        
        // 3. Consumer - consumes a value (returns void)
        Consumer printer = System.out::println;
        printer.accept("Hello Consumer!");
        
        // Consumer chain
        Consumer logger = s -> System.out.println("LOG: " + s);
        Consumer combined = printer.andThen(logger);
        combined.accept("Important Message");
        
        // 4. Supplier - supplies a value
        Supplier randomSupplier = Math::random;
        System.out.println("Random number: " + randomSupplier.get());
        
        Supplier> listSupplier = ArrayList::new;
        List newList = listSupplier.get();
        newList.add("First Item");
        System.out.println("New list: " + newList);
        
        // 5. UnaryOperator - function where input and output are same type
        UnaryOperator toUpper = String::toUpperCase;
        System.out.println("hello in uppercase: " + toUpper.apply("hello"));
        
        // 6. BinaryOperator - operation on two operands of same type
        BinaryOperator maxOperator = Integer::max;
        System.out.println("Max of 5 and 10: " + maxOperator.apply(5, 10));
        
        // 7. BiFunction - two arguments, returns result
        BiFunction concat = (s1, s2) -> s1 + " " + s2;
        System.out.println("Concatenated: " + concat.apply("Hello", "World"));
    }
    
    // === VARIABLE CAPTURE IN LAMBDA ===
    
    public void demonstrateVariableCapture() {
        System.out.println("\n=== VARIABLE CAPTURE IN LAMBDA ===\n");
        
        // Instance variable - can be modified
        this.instanceCounter = 0;
        Runnable instanceLambda = () -> {
            instanceCounter++;
            System.out.println("Instance counter: " + instanceCounter);
        };
        instanceLambda.run();
        instanceLambda.run();
        
        // Static variable - can be modified
        staticCounter = 0;
        Runnable staticLambda = () -> {
            staticCounter++;
            System.out.println("Static counter: " + staticCounter);
        };
        staticLambda.run();
        staticLambda.run();
        
        // Local variable - must be effectively final
        final String finalLocal = "Final Local";
        String effectivelyFinal = "Effectively Final";
        // effectivelyFinal = "Changed"; // Would break the lambda
        
        Consumer localLambda = prefix -> {
            System.out.println(prefix + ": " + finalLocal);
            System.out.println(prefix + ": " + effectivelyFinal);
            // Can access but not modify captured locals
        };
        localLambda.accept("Local Capture");
        
        // Array elements can be modified (array reference is effectively final)
        int[] numbers = {1, 2, 3};
        Runnable arrayLambda = () -> {
            numbers[0] = 100; // Can modify array elements
            System.out.println("Modified array: " + Arrays.toString(numbers));
        };
        arrayLambda.run();
    }
    
    // === METHOD REFERENCES ===
    
    public void demonstrateMethodReferences() {
        System.out.println("\n=== METHOD REFERENCES ===\n");
        
        List names = Arrays.asList("Alice", "Bob", "Charlie", "David");
        
        // 1. Static method reference
        names.forEach(System.out::println);
        
        // 2. Instance method reference on particular instance
        String prefix = "Hello ";
        names.forEach(prefix::concat);
        
        // 3. Instance method reference on arbitrary instance
        names.sort(String::compareToIgnoreCase);
        System.out.println("Sorted: " + names);
        
        // 4. Constructor reference
        Supplier> listSupplier = ArrayList::new;
        List newList = listSupplier.get();
        newList.add("New Item");
        System.out.println("New list: " + newList);
        
        // 5. Array constructor reference
        IntFunction arrayConstructor = int[]::new;
        int[] intArray = arrayConstructor.apply(5);
        System.out.println("Array length: " + intArray.length);
        
        // Practical examples
        System.out.println("\nPractical Examples:");
        
        // Convert list of strings to uppercase
        List upperNames = names.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
        System.out.println("Uppercase: " + upperNames);
        
        // Create map from string to its length
        Map lengthMap = names.stream()
            .collect(Collectors.toMap(
                Function.identity(),  // key: string itself
                String::length        // value: length of string
            ));
        System.out.println("Length map: " + lengthMap);
    }
    
    // === STREAMS AND LAMBDA ===
    
    public void demonstrateStreams() {
        System.out.println("\n=== STREAMS WITH LAMBDA ===\n");
        
        List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // 1. Filter even numbers
        List evens = numbers.stream()
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList());
        System.out.println("Even numbers: " + evens);
        
        // 2. Map to squares
        List squares = numbers.stream()
            .map(n -> n * n)
            .collect(Collectors.toList());
        System.out.println("Squares: " + squares);
        
        // 3. Reduce to sum
        int sum = numbers.stream()
            .reduce(0, Integer::sum);
        System.out.println("Sum: " + sum);
        
        // 4. Find max
        Optional max = numbers.stream()
            .max(Integer::compare);
        max.ifPresent(m -> System.out.println("Max: " + m));
        
        // 5. Complex stream operation
        List result = numbers.stream()
            .filter(n -> n > 5)                    // Keep numbers > 5
            .map(n -> n * 2)                       // Double them
            .sorted((a, b) -> b.compareTo(a))      // Sort descending
            .map(n -> "Value: " + n)               // Convert to string
            .collect(Collectors.toList());         // Collect to list
        System.out.println("Complex result: " + result);
        
        // 6. Parallel stream
        long parallelCount = numbers.parallelStream()
            .filter(n -> n % 3 == 0)
            .count();
        System.out.println("Numbers divisible by 3: " + parallelCount);
    }
    
    // === PRACTICAL EXAMPLE: EVENT PROCESSING SYSTEM ===
    
    class Event {
        String type;
        String data;
        long timestamp;
        
        Event(String type, String data) {
            this.type = type;
            this.data = data;
            this.timestamp = System.currentTimeMillis();
        }
        
        @Override
        public String toString() {
            return type + ": " + data + " @ " + timestamp;
        }
    }
    
    @FunctionalInterface
    interface EventHandler {
        void handle(Event event);
        
        default EventHandler andThen(EventHandler after) {
            return event -> {
                this.handle(event);
                after.handle(event);
            };
        }
    }
    
    class EventProcessor {
        private Map> handlers = new HashMap<>();
        
        public void registerHandler(String eventType, EventHandler handler) {
            handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
        }
        
        public void processEvent(Event event) {
            List eventHandlers = handlers.get(event.type);
            if (eventHandlers != null) {
                eventHandlers.forEach(handler -> handler.handle(event));
            }
        }
        
        public void processEvents(List events) {
            events.forEach(this::processEvent);
        }
    }
    
    public void demonstrateEventProcessing() {
        System.out.println("\n=== EVENT PROCESSING WITH LAMBDA ===\n");
        
        EventProcessor processor = new EventProcessor();
        
        // Register handlers using lambda expressions
        processor.registerHandler("LOGIN", event -> 
            System.out.println("Login event: " + event.data));
        
        processor.registerHandler("LOGOUT", event -> 
            System.out.println("Logout event: " + event.data));
        
        processor.registerHandler("ERROR", event -> 
            System.err.println("ERROR: " + event.data));
        
        // More complex handler
        processor.registerHandler("PURCHASE", event -> {
            System.out.println("Processing purchase...");
            System.out.println("Data: " + event.data);
            System.out.println("Timestamp: " + event.timestamp);
        });
        
        // Chained handlers
        EventHandler logger = event -> 
            System.out.println("LOG: " + event.type + " - " + event.data);
            
        EventHandler notifier = event -> 
            System.out.println("NOTIFY: Event processed - " + event.type);
            
        processor.registerHandler("SYSTEM", logger.andThen(notifier));
        
        // Create and process events
        List events = Arrays.asList(
            new Event("LOGIN", "User Alice logged in"),
            new Event("PURCHASE", "Item: Laptop, Amount: $999.99"),
            new Event("ERROR", "Connection timeout"),
            new Event("LOGOUT", "User Alice logged out"),
            new Event("SYSTEM", "System maintenance scheduled")
        );
        
        processor.processEvents(events);
        
        // Process with stream
        System.out.println("\n=== PROCESSING WITH STREAM ===");
        events.stream()
            .filter(e -> !e.type.equals("ERROR"))
            .sorted(Comparator.comparingLong(e -> e.timestamp))
            .forEach(e -> System.out.println("Processed: " + e));
    }
    
    // === LAMBDA BEST PRACTICES ===
    
    public void demonstrateBestPractices() {
        System.out.println("\n=== LAMBDA BEST PRACTICES ===\n");
        
        // 1. Keep lambdas short and focused
        List names = Arrays.asList("Alice", "Bob", "Charlie");
        
        // Good: Simple lambda
        names.forEach(name -> System.out.println("Hello, " + name));
        
        // Bad: Complex logic in lambda (should be extracted)
        names.forEach(name -> {
            String formatted = name.toUpperCase();
            int length = formatted.length();
            System.out.println(formatted + " has " + length + " characters");
            // Too complex for lambda - consider extracting method
        });
        
        // 2. Use method references when possible
        List numbers = Arrays.asList(1, 2, 3);
        
        // Prefer this:
        numbers.forEach(System.out::println);
        
        // Over this:
        numbers.forEach(n -> System.out.println(n));
        
        // 3. Avoid modifying captured variables
        final int[] counter = {0}; // Use array hack if needed
        names.forEach(name -> {
            counter[0]++; // Avoid if possible
            System.out.println(counter[0] + ": " + name);
        });
        
        // Better: Use stream with count
        long count = names.stream().count();
        System.out.println("Total names: " + count);
        
        // 4. Use meaningful parameter names
        BinaryOperator adder = (first, second) -> first + second;
        // Better than (a, b) -> a + b
        
        // 5. Consider extracting complex lambdas to methods
        Predicate isLongString = this::checkStringLength;
        System.out.println("Is 'Hello' long? " + isLongString.test("Hello"));
    }
    
    private boolean checkStringLength(String s) {
        return s != null && s.length() > 10;
    }
    
    // Instance and static variables for capture demo
    private int instanceCounter;
    private static int staticCounter;
    
    public static void main(String[] args) {
        LambdaExpressionsComplete demo = new LambdaExpressionsComplete();
        
        // Demonstrate syntax variations
        demo.demonstrateSyntax();
        
        // Demonstrate built-in functional interfaces
        demo.demonstrateBuiltInInterfaces();
        
        // Demonstrate variable capture
        demo.demonstrateVariableCapture();
        
        // Demonstrate method references
        demo.demonstrateMethodReferences();
        
        // Demonstrate streams
        demo.demonstrateStreams();
        
        // Practical example
        demo.demonstrateEventProcessing();
        
        // Best practices
        demo.demonstrateBestPractices();
        
        System.out.println("\n=== KEY TAKEAWAYS ===\n");
        System.out.println("1. Lambdas enable functional programming in Java");
        System.out.println("2. They're more concise than anonymous classes");
        System.out.println("3. Can only be used with functional interfaces");
        System.out.println("4. Can capture effectively final variables");
        System.out.println("5. Work seamlessly with streams and method references");
        System.out.println("6. Improve code readability when used appropriately");
        
        System.out.println("\n=== WHEN TO USE LAMBDA ===\n");
        System.out.println("✅ Simple functional interface implementations");
        System.out.println("✅ Event handlers and callbacks");
        System.out.println("✅ Comparators and predicates");
        System.out.println("✅ Stream operations");
        System.out.println("✅ Anywhere you'd use a single-method anonymous class");
        
        System.out.println("\n=== WHEN NOT TO USE LAMBDA ===\n");
        System.out.println("❌ Complex logic (extract to method)");
        System.out.println("❌ Multiple method implementations");
        System.out.println("❌ When you need instance variables or this reference");
        System.out.println("❌ When readability suffers");
    }
}

5. Real-World Examples & Patterns

Complete E-commerce System with Inner Classes
// Complete e-commerce system demonstrating all types of inner classes
import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class ECommerceInnerClasses {
    
    // === OUTER CLASS: E-COMMERCE STORE ===
    
    private String storeName;
    private List products;
    private List orders;
    private Map customers;
    
    public ECommerceInnerClasses(String storeName) {
        this.storeName = storeName;
        this.products = new ArrayList<>();
        this.orders = new ArrayList<>();
        this.customers = new HashMap<>();
        initializeSampleData();
    }
    
    // === INNER CLASS: PRODUCT ===
    
    class Product {
        private String id;
        private String name;
        private String description;
        private double price;
        private int stock;
        private Category category;
        
        // Builder as static nested class
        static class Builder {
            private String id;
            private String name;
            private String description;
            private double price;
            private int stock;
            private Category category;
            
            public Builder(String id, String name) {
                this.id = id;
                this.name = name;
            }
            
            public Builder description(String description) {
                this.description = description;
                return this;
            }
            
            public Builder price(double price) {
                this.price = price;
                return this;
            }
            
            public Builder stock(int stock) {
                this.stock = stock;
                return this;
            }
            
            public Builder category(Category category) {
                this.category = category;
                return this;
            }
            
            public Product build() {
                return new Product(this);
            }
        }
        
        private Product(Builder builder) {
            this.id = builder.id;
            this.name = builder.name;
            this.description = builder.description;
            this.price = builder.price;
            this.stock = builder.stock;
            this.category = builder.category;
        }
        
        // Local class for discount calculation
        public double calculateDiscountedPrice(Customer customer) {
            class DiscountCalculator {
                private double basePrice;
                private Customer customer;
                
                DiscountCalculator(double basePrice, Customer customer) {
                    this.basePrice = basePrice;
                    this.customer = customer;
                }
                
                double calculate() {
                    double discount = 0;
                    
                    // Customer type discount
                    if (customer.getType() == CustomerType.PREMIUM) {
                        discount += 0.10; // 10% for premium
                    } else if (customer.getType() == CustomerType.VIP) {
                        discount += 0.20; // 20% for VIP
                    }
                    
                    // Category discount
                    if (category == Category.ELECTRONICS) {
                        discount += 0.05; // 5% for electronics
                    }
                    
                    return basePrice * (1 - discount);
                }
            }
            
            DiscountCalculator calculator = new DiscountCalculator(price, customer);
            return calculator.calculate();
        }
        
        // Getters
        public String getId() { return id; }
        public String getName() { return name; }
        public double getPrice() { return price; }
        public int getStock() { return stock; }
        public Category getCategory() { return category; }
        
        @Override
        public String toString() {
            return name + " ($" + price + ")";
        }
    }
    
    // === STATIC NESTED ENUMS ===
    
    static enum Category {
        ELECTRONICS, CLOTHING, BOOKS, HOME, SPORTS
    }
    
    static enum CustomerType {
        REGULAR, PREMIUM, VIP
    }
    
    static enum OrderStatus {
        PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED
    }
    
    // === INNER CLASS: CUSTOMER ===
    
    class Customer {
        private String id;
        private String name;
        private String email;
        private CustomerType type;
        private List orderHistory;
        
        public Customer(String id, String name, String email, CustomerType type) {
            this.id = id;
            this.name = name;
            this.email = email;
            this.type = type;
            this.orderHistory = new ArrayList<>();
        }
        
        // Anonymous class for email notification
        interface Notification {
            void send(String subject, String message);
        }
        
        public void sendWelcomeEmail() {
            Notification emailNotifier = new Notification() {
                @Override
                public void send(String subject, String message) {
                    System.out.println("=== Sending Email ===");
                    System.out.println("To: " + email);
                    System.out.println("Subject: " + subject);
                    System.out.println("Message: " + message);
                    System.out.println("====================\n");
                }
            };
            
            emailNotifier.send(
                "Welcome to " + storeName,
                "Dear " + name + ",\n\nWelcome to our store! As a " + type + 
                " customer, you'll enjoy special benefits."
            );
        }
        
        // Lambda for different notification types
        public void notify(Consumer notifier, String message) {
            notifier.accept(message);
        }
        
        public void addOrder(Order order) {
            orderHistory.add(order);
        }
        
        // Getters
        public String getId() { return id; }
        public String getName() { return name; }
        public CustomerType getType() { return type; }
        public List getOrderHistory() { return orderHistory; }
    }
    
    // === INNER CLASS: ORDER ===
    
    class Order {
        private String orderId;
        private Customer customer;
        private List items;
        private OrderStatus status;
        private Date orderDate;
        private double totalAmount;
        
        // Builder pattern
        static class Builder {
            private String orderId;
            private Customer customer;
            private List items = new ArrayList<>();
            
            public Builder(String orderId, Customer customer) {
                this.orderId = orderId;
                this.customer = customer;
            }
            
            public Builder addItem(Product product, int quantity) {
                items.add(new OrderItem(product, quantity));
                return this;
            }
            
            public Order build() {
                return new Order(this);
            }
        }
        
        // Local class for order item
        class OrderItem {
            private Product product;
            private int quantity;
            private double itemTotal;
            
            OrderItem(Product product, int quantity) {
                this.product = product;
                this.quantity = quantity;
                this.itemTotal = product.getPrice() * quantity;
            }
            
            public double getItemTotal() {
                return itemTotal;
            }
            
            @Override
            public String toString() {
                return product.getName() + " x" + quantity + " = $" + itemTotal;
            }
        }
        
        private Order(Builder builder) {
            this.orderId = builder.orderId;
            this.customer = builder.customer;
            this.items = builder.items;
            this.status = OrderStatus.PENDING;
            this.orderDate = new Date();
            this.totalAmount = calculateTotal();
        }
        
        private double calculateTotal() {
            return items.stream()
                .mapToDouble(OrderItem::getItemTotal)
                .sum();
        }
        
        // Iterator as inner class
        class OrderIterator implements Iterator {
            private int currentIndex = 0;
            
            @Override
            public boolean hasNext() {
                return currentIndex < items.size();
            }
            
            @Override
            public OrderItem next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                return items.get(currentIndex++);
            }
        }
        
        public Iterator iterator() {
            return new OrderIterator();
        }
        
        // Anonymous class for status change listener
        interface StatusChangeListener {
            void onStatusChange(OrderStatus oldStatus, OrderStatus newStatus);
        }
        
        private List listeners = new ArrayList<>();
        
        public void addStatusChangeListener(StatusChangeListener listener) {
            listeners.add(listener);
        }
        
        public void setStatus(OrderStatus newStatus) {
            OrderStatus oldStatus = this.status;
            this.status = newStatus;
            
            // Notify all listeners
            for (StatusChangeListener listener : listeners) {
                listener.onStatusChange(oldStatus, newStatus);
            }
        }
        
        // Getters
        public String getOrderId() { return orderId; }
        public Customer getCustomer() { return customer; }
        public OrderStatus getStatus() { return status; }
        public double getTotalAmount() { return totalAmount; }
        public List getItems() { return items; }
        
        @Override
        public String toString() {
            return "Order #" + orderId + " - " + status + " - $" + totalAmount;
        }
    }
    
    // === SEARCH FUNCTIONALITY WITH LAMBDA ===
    
    interface ProductFilter {
        boolean test(Product product);
    }
    
    public List searchProducts(ProductFilter filter) {
        return products.stream()
            .filter(filter::test)
            .collect(Collectors.toList());
    }
    
    // Factory method for common filters using lambda
    public static ProductFilter createPriceFilter(double maxPrice) {
        return product -> product.getPrice() <= maxPrice;
    }
    
    public static ProductFilter createCategoryFilter(Category category) {
        return product -> product.getCategory() == category;
    }
    
    public static ProductFilter createStockFilter(int minStock) {
        return product -> product.getStock() >= minStock;
    }
    
    // === SHOPPING CART WITH INNER CLASSES ===
    
    class ShoppingCart {
        private Customer customer;
        private Map items;
        
        public ShoppingCart(Customer customer) {
            this.customer = customer;
            this.items = new HashMap<>();
        }
        
        // Local class for cart item
        class CartItem {
            Product product;
            int quantity;
            
            CartItem(Product product, int quantity) {
                this.product = product;
                this.quantity = quantity;
            }
            
            double getSubtotal() {
                return product.calculateDiscountedPrice(customer) * quantity;
            }
        }
        
        public void addItem(Product product, int quantity) {
            items.merge(product, quantity, Integer::sum);
        }
        
        public void removeItem(Product product) {
            items.remove(product);
        }
        
        public double calculateTotal() {
            return items.entrySet().stream()
                .mapToDouble(entry -> {
                    Product product = entry.getKey();
                    int quantity = entry.getValue();
                    return product.calculateDiscountedPrice(customer) * quantity;
                })
                .sum();
        }
        
        // Anonymous class for checkout
        public Order checkout() {
            // Validate stock
            for (Map.Entry entry : items.entrySet()) {
                Product product = entry.getKey();
                int requested = entry.getValue();
                
                if (product.getStock() < requested) {
                    throw new IllegalStateException(
                        "Insufficient stock for " + product.getName()
                    );
                }
            }
            
            // Create order
            Order.Builder orderBuilder = new Order.Builder(
                "ORD" + System.currentTimeMillis(),
                customer
            );
            
            items.forEach(orderBuilder::addItem);
            Order order = orderBuilder.build();
            
            // Update stock
            items.forEach((product, quantity) -> {
                // This would normally update database
                System.out.println("Reducing stock for " + product.getName() + 
                                 " by " + quantity);
            });
            
            // Add status change listener using lambda
            order.addStatusChangeListener((oldStatus, newStatus) -> {
                System.out.println("Order " + order.getOrderId() + 
                                 " changed from " + oldStatus + 
                                 " to " + newStatus);
            });
            
            orders.add(order);
            customer.addOrder(order);
            items.clear();
            
            return order;
        }
        
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("Shopping Cart for ").append(customer.getName()).append(":\n");
            items.forEach((product, quantity) -> {
                double subtotal = product.calculateDiscountedPrice(customer) * quantity;
                sb.append("  ").append(product.getName())
                  .append(" x").append(quantity)
                  .append(" = $").append(subtotal)
                  .append("\n");
            });
            sb.append("Total: $").append(calculateTotal());
            return sb.toString();
        }
    }
    
    // === STORE OPERATIONS ===
    
    public void addProduct(Product product) {
        products.add(product);
    }
    
    public void registerCustomer(Customer customer) {
        customers.put(customer.getId(), customer);
        customer.sendWelcomeEmail();
    }
    
    public Customer getCustomer(String id) {
        return customers.get(id);
    }
    
    public ShoppingCart createShoppingCart(Customer customer) {
        return new ShoppingCart(customer);
    }
    
    public List getCustomerOrders(String customerId) {
        Customer customer = customers.get(customerId);
        return customer != null ? customer.getOrderHistory() : Collections.emptyList();
    }
    
    // Report generation with local class
    public void generateSalesReport() {
        class SalesReport {
            double totalRevenue;
            int totalOrders;
            Map revenueByCategory;
            Map revenueByCustomerType;
            
            SalesReport() {
                this.totalRevenue = orders.stream()
                    .mapToDouble(Order::getTotalAmount)
                    .sum();
                this.totalOrders = orders.size();
                this.revenueByCategory = new HashMap<>();
                this.revenueByCustomerType = new HashMap<>();
                
                // Calculate revenue by category
                for (Order order : orders) {
                    for (Order.OrderItem item : order.getItems()) {
                        Category cat = item.product.getCategory();
                        revenueByCategory.merge(cat, item.getItemTotal(), Double::sum);
                    }
                    
                    // Calculate revenue by customer type
                    CustomerType type = order.getCustomer().getType();
                    revenueByCustomerType.merge(type, order.getTotalAmount(), Double::sum);
                }
            }
            
            void print() {
                System.out.println("\n=== SALES REPORT ===");
                System.out.println("Total Revenue: $" + totalRevenue);
                System.out.println("Total Orders: " + totalOrders);
                System.out.println("\nRevenue by Category:");
                revenueByCategory.forEach((cat, rev) -> 
                    System.out.println("  " + cat + ": $" + rev));
                System.out.println("\nRevenue by Customer Type:");
                revenueByCustomerType.forEach((type, rev) -> 
                    System.out.println("  " + type + ": $" + rev));
            }
        }
        
        SalesReport report = new SalesReport();
        report.print();
    }
    
    // Initialize sample data
    private void initializeSampleData() {
        // Create products
        products.add(new Product.Builder("P001", "Laptop")
            .description("High-performance laptop")
            .price(999.99)
            .stock(50)
            .category(Category.ELECTRONICS)
            .build());
            
        products.add(new Product.Builder("P002", "T-Shirt")
            .description("Cotton t-shirt")
            .price(19.99)
            .stock(200)
            .category(Category.CLOTHING)
            .build());
            
        products.add(new Product.Builder("P003", "Java Programming Book")
            .description("Learn Java programming")
            .price(49.99)
            .stock(100)
            .category(Category.BOOKS)
            .build());
        
        // Register customers
        registerCustomer(new Customer("C001", "Alice Johnson", 
                                     "alice@email.com", CustomerType.PREMIUM));
        registerCustomer(new Customer("C002", "Bob Smith", 
                                     "bob@email.com", CustomerType.REGULAR));
        registerCustomer(new Customer("C003", "Charlie Brown", 
                                     "charlie@email.com", CustomerType.VIP));
    }
    
    public static void main(String[] args) {
        System.out.println("=== E-COMMERCE SYSTEM WITH INNER CLASSES ===\n");
        
        ECommerceInnerClasses store = new ECommerceInnerClasses("TechMart");
        
        // Get customers
        Customer alice = store.getCustomer("C001");
        Customer bob = store.getCustomer("C002");
        
        // Create shopping carts
        ShoppingCart aliceCart = store.createShoppingCart(alice);
        ShoppingCart bobCart = store.createShoppingCart(bob);
        
        // Get products
        Product laptop = store.products.get(0);
        Product tshirt = store.products.get(1);
        Product book = store.products.get(2);
        
        // Add items to carts
        System.out.println("=== ADDING ITEMS TO CARTS ===\n");
        
        aliceCart.addItem(laptop, 1);
        aliceCart.addItem(book, 2);
        System.out.println(aliceCart);
        
        bobCart.addItem(tshirt, 3);
        bobCart.addItem(book, 1);
        System.out.println("\n" + bobCart);
        
        // Checkout
        System.out.println("\n=== CHECKOUT ===\n");
        
        try {
            Order aliceOrder = aliceCart.checkout();
            System.out.println("Alice's order created: " + aliceOrder);
            aliceOrder.setStatus(OrderStatus.PROCESSING);
            aliceOrder.setStatus(OrderStatus.SHIPPED);
            
            Order bobOrder = bobCart.checkout();
            System.out.println("Bob's order created: " + bobOrder);
        } catch (Exception e) {
            System.out.println("Checkout error: " + e.getMessage());
        }
        
        // Search products using lambda
        System.out.println("\n=== SEARCH PRODUCTS ===\n");
        
        System.out.println("Products under $50:");
        List cheapProducts = store.searchProducts(
            createPriceFilter(50.0)
        );
        cheapProducts.forEach(p -> System.out.println("  " + p));
        
        System.out.println("\nElectronics products:");
        List electronics = store.searchProducts(
            createCategoryFilter(Category.ELECTRONICS)
        );
        electronics.forEach(p -> System.out.println("  " + p));
        
        // Custom search with lambda
        System.out.println("\nBooks in stock:");
        List booksInStock = store.searchProducts(
            p -> p.getCategory() == Category.BOOKS && p.getStock() > 0
        );
        booksInStock.forEach(p -> System.out.println("  " + p));
        
        // Generate report
        store.generateSalesReport();
        
        System.out.println("\n=== INNER CLASSES USED IN THIS SYSTEM ===\n");
        System.out.println("1. Inner Class: Product (accesses store state)");
        System.out.println("2. Static Nested Class: Builder pattern");
        System.out.println("3. Local Class: DiscountCalculator, SalesReport");
        System.out.println("4. Anonymous Class: Email notification, Event listeners");
        System.out.println("5. Lambda Expressions: Search filters, Stream operations");
        System.out.println("6. Iterator Pattern: Order iterator as inner class");
        
        System.out.println("\n=== BENEFITS DEMONSTRATED ===\n");
        System.out.println("✅ Encapsulation: Inner classes access outer private members");
        System.out.println("✅ Organization: Related classes grouped together");
        System.out.println("✅ Readability: Code is more cohesive");
        System.out.println("✅ Flexibility: Easy to add new functionality");
        System.out.println("✅ Maintainability: Changes localized to relevant areas");
    }
}

6. Best Practices & Common Pitfalls

Common Inner Classes Mistakes:
  1. Memory Leaks: Inner classes holding references to outer instances
  2. Serialization Issues: Inner classes have synthetic fields
  3. Overusing Inner Classes: Making code harder to understand
  4. Static Context Errors: Trying to access instance members from static nested class
  5. Anonymous Class Bloat: Creating large anonymous classes
  6. Capturing Mutable Variables: Modifying captured variables
Inner Classes Best Practices
  • Use static nested classes by default
  • Only use inner classes when need access to outer instance
  • Keep inner classes small and focused
  • Avoid deep nesting (more than 2 levels)
  • Use lambda expressions instead of anonymous classes when possible
  • Be mindful of serialization requirements
  • Document the relationship between inner and outer classes
Performance Guidelines
  • Inner classes have memory overhead (outer reference)
  • Static nested classes are more memory efficient
  • Avoid creating many inner class instances in loops
  • Lambdas are generally faster than anonymous classes
  • Consider using static nested classes for frequently instantiated helpers
  • Profile before optimizing inner class usage

When to Use Each Type of Inner Class

Static Nested Class: Default choice - use when class is logically related but doesn't need outer instance
Inner Class: When helper needs access to outer instance state (iterators, adapters)
Local Class: When class is only used within one method and needs local variables
Anonymous Class: One-time implementations, event listeners (use lambda when possible)
Lambda: Single-method interfaces, functional programming, stream operations

InnerClassesChecklist.java
import java.io.*;
import java.util.*;

public class InnerClassesChecklist {
    
    public static void innerClassesChecklist() {
        System.out.println("=== INNER CLASSES IMPLEMENTATION CHECKLIST ===\n");
        
        System.out.println("✅ 1. Choose the Right Type");
        System.out.println("   - Does it need access to outer instance? → Inner Class");
        System.out.println("   - Is it logically related but independent? → Static Nested");
        System.out.println("   - Used only in one method? → Local Class");
        System.out.println("   - One-time implementation? → Anonymous/Lambda");
        
        System.out.println("\n✅ 2. Memory Considerations");
        System.out.println("   - Inner classes hold reference to outer (potential leaks)");
        System.out.println("   - Static nested classes are more memory efficient");
        System.out.println("   - Consider weak references if needed");
        
        System.out.println("\n✅ 3. Serialization Readiness");
        System.out.println("   - Inner classes have synthetic fields");
        System.out.println("   - May need custom readObject/writeObject");
        System.out.println("   - Consider static nested classes for serialization");
        
        System.out.println("\n✅ 4. Access Control");
        System.out.println("   - Use appropriate access modifiers");
        System.out.println("   - Private inner classes for implementation details");
        System.out.println("   - Protected for extension in same package");
        
        System.out.println("\n✅ 5. Testing Strategy");
        System.out.println("   - Test inner classes independently when possible");
        System.out.println("   - Mock outer instance for inner class testing");
        System.out.println("   - Test serialization if required");
        
        System.out.println("\n✅ 6. Documentation");
        System.out.println("   - Document relationship to outer class");
        System.out.println("   - Explain when to use static vs non-static");
        System.out.println("   - Note any serialization considerations");
        
        System.out.println("\n✅ 7. Code Review Points");
        System.out.println("   - Is nesting necessary?");
        System.out.println("   - Could it be a top-level class?");
        System.out.println("   - Is the coupling appropriate?");
    }
    
    // Example: Well-designed inner class usage
    
    // Outer class designed for extension with inner classes
    static abstract class DataProcessor {
        protected String dataSource;
        
        public DataProcessor(String dataSource) {
            this.dataSource = dataSource;
        }
        
        // Template method using inner class
        public final void process() {
            Validator validator = createValidator();
            if (!validator.validate()) {
                throw new IllegalArgumentException("Invalid data");
            }
            
            Processor processor = createProcessor();
            processor.processData();
            
            Notifier notifier = createNotifier();
            notifier.notifyCompletion();
        }
        
        // Factory methods to be implemented by subclasses
        protected abstract Validator createValidator();
        protected abstract Processor createProcessor();
        protected abstract Notifier createNotifier();
        
        // Inner interface for validator
        protected interface Validator {
            boolean validate();
        }
        
        // Inner interface for processor
        protected interface Processor {
            void processData();
        }
        
        // Inner interface for notifier
        protected interface Notifier {
            void notifyCompletion();
        }
    }
    
    // Concrete implementation
    static class FileProcessor extends DataProcessor {
        
        public FileProcessor(String filePath) {
            super(filePath);
        }
        
        @Override
        protected Validator createValidator() {
            // Local class for file validation
            class FileValidator implements Validator {
                @Override
                public boolean validate() {
                    File file = new File(dataSource);
                    return file.exists() && file.canRead();
                }
            }
            return new FileValidator();
        }
        
        @Override
        protected Processor createProcessor() {
            // Lambda for simple processing
            return () -> {
                System.out.println("Processing file: " + dataSource);
                // Actual file processing logic
            };
        }
        
        @Override
        protected Notifier createNotifier() {
            // Anonymous class for notification
            return new Notifier() {
                @Override
                public void notifyCompletion() {
                    System.out.println("File processing completed: " + dataSource);
                    // Additional notification logic
                }
            };
        }
    }
    
    // Example: Avoiding common pitfalls
    
    static class PitfallExamples {
        
        // PITFALL 1: Memory leak with inner class
        static class MemoryLeakExample {
            private byte[] largeData = new byte[1000000]; // 1MB
            private List callbacks = new ArrayList<>();
            
            // Inner class holding reference to outer
            class Callback implements Runnable {
                @Override
                public void run() {
                    System.out.println("Callback using: " + largeData.length);
                }
            }
            
            public void addCallback() {
                callbacks.add(new Callback()); // Keeps reference to outer instance
            }
            
            // Solution: Use static nested class with weak reference
            static class SafeCallback implements Runnable {
                private WeakReference outerRef;
                
                SafeCallback(MemoryLeakExample outer) {
                    this.outerRef = new WeakReference<>(outer);
                }
                
                @Override
                public void run() {
                    MemoryLeakExample outer = outerRef.get();
                    if (outer != null) {
                        System.out.println("Safe callback");
                    }
                }
            }
        }
        
        // PITFALL 2: Serialization issues
        static class SerializationExample implements Serializable {
            private String data = "Important Data";
            
            // Non-static inner class - serialization problem!
            class InnerData implements Serializable {
                private String inner = "Inner Data";
            }
            
            // Solution: Make it static
            static class SerializableData implements Serializable {
                private String data = "Serializable Data";
            }
        }
        
        // PITFALL 3: Overly complex anonymous class
        static class ComplexAnonymous {
            public void problematicMethod() {
                // Too complex for anonymous class
                Runnable complexTask = new Runnable() {
                    private int state = 0;
                    private List buffer = new ArrayList<>();
                    
                    @Override
                    public void run() {
                        // 50 lines of complex logic...
                        System.out.println("Too complex for anonymous class");
                    }
                    
                    private void helperMethod() {
                        // Even more complexity...
                    }
                };
                
                // Solution: Extract to named class
                complexTask.run();
            }
            
            // Better: Extract to static nested class
            static class ComplexTask implements Runnable {
                private int state = 0;
                private List buffer = new ArrayList<>();
                
                @Override
                public void run() {
                    // Complex logic here
                }
                
                private void helperMethod() {
                    // Helper methods
                }
            }
        }
    }
    
    public static void main(String[] args) {
        innerClassesChecklist();
        
        System.out.println("\n=== EXAMPLE: WELL-DESIGNED DATA PROCESSOR ===\n");
        
        DataProcessor processor = new FileProcessor("data.txt");
        processor.process();
        
        System.out.println("\n=== COMMON PITFALLS AND SOLUTIONS ===\n");
        
        System.out.println("PITFALL 1: Memory Leaks");
        System.out.println("Problem: Inner classes hold strong references to outer");
        System.out.println("Solution: Use static nested classes with WeakReference");
        
        System.out.println("\nPITFALL 2: Serialization Issues");
        System.out.println("Problem: Inner classes have synthetic fields");
        System.out.println("Solution: Use static nested classes for serialization");
        
        System.out.println("\nPITFALL 3: Anonymous Class Complexity");
        System.out.println("Problem: Large anonymous classes are hard to read/maintain");
        System.out.println("Solution: Extract to named static nested class");
        
        System.out.println("\nPITFALL 4: Static Context Errors");
        System.out.println("Problem: Trying to access instance members from static nested class");
        System.out.println("Solution: Pass outer instance as parameter if needed");
        
        System.out.println("\nPITFALL 5: Capturing Mutable Variables");
        System.out.println("Problem: Modifying captured variables in lambda/local class");
        System.out.println("Solution: Use atomic references or avoid modification");
        
        System.out.println("\n=== FINAL RECOMMENDATIONS ===\n");
        System.out.println("1. Prefer static nested classes over inner classes");
        System.out.println("2. Use lambdas instead of anonymous classes when possible");
        System.out.println("3. Keep inner classes small and focused");
        System.out.println("4. Be mindful of memory and serialization implications");
        System.out.println("5. Document the design decisions for inner classes");
        System.out.println("6. Test inner classes thoroughly, especially for memory leaks");
        
        System.out.println("\n=== WHEN TO REEVALUATE ===\n");
        System.out.println("Consider moving to top-level class when:");
        System.out.println("- Inner class grows beyond 100 lines");
        System.out.println("- Inner class is used by multiple outer classes");
        System.out.println("- Inner class has complex dependencies");
        System.out.println("- Testing becomes difficult");
    }
}