Index

Inner and Nested Classes

Java for Beginners

13.1 Member and Static Nested Classes

Java allows you to define classes inside other classes, which are called nested classes. Nested classes help logically group classes that are only used in one place, increase encapsulation, and reduce namespace clutter. There are two main types of nested classes: member inner classes and static nested classes. Though both are nested inside another class, they behave quite differently.

Member Inner Classes

A member inner class is a non-static class defined inside another class. It is associated with an instance of the enclosing class and can access the enclosing class’s members—including private fields and methods—directly.

Syntax and Characteristics:

public class OuterClass {
    class InnerClass {
        void display() {
            System.out.println("Inside member inner class");
        }
    }
}

Example: Member Inner Class Usage

public class Car {
    private String model = "Tesla";

    // Member inner class
    class Engine {
        void showModel() {
            // Accessing outer class's private field
            System.out.println("Engine in car model: " + model);
        }
    }

    public static void main(String[] args) {
        Car car = new Car();               // Create outer class instance
        Car.Engine engine = car.new Engine();  // Create inner class instance
        engine.showModel();                // Output: Engine in car model: Tesla
    }
}

Static Nested Classes

A static nested class is a nested class declared with the static keyword. Unlike member inner classes, it does not associate with an instance of the outer class. Instead, it behaves more like a regular top-level class but scoped inside the outer class.

Syntax and Characteristics:

public class OuterClass {
    static class StaticNestedClass {
        void display() {
            System.out.println("Inside static nested class");
        }
    }
}

Example: Static Nested Class Usage

public class Computer {
    private static String brand = "Dell";

    // Static nested class
    static class USBPort {
        void showBrand() {
            // Can access static fields of outer class
            System.out.println("USB Port for brand: " + brand);
        }
    }

    public static void main(String[] args) {
        Computer.USBPort usb = new Computer.USBPort();  // No outer instance needed
        usb.showBrand();  // Output: USB Port for brand: Dell
    }
}

Key Differences and Use Cases

Feature Member Inner Class Static Nested Class
Declared with static? No Yes
Associated with outer instance? Yes No
Access to outer class members Can access all (including non-static) Can access only static members
Instantiation requirement Needs an instance of outer class No outer instance needed
Typical use case When inner class needs outer instance data When grouping helper classes logically

Why Use Static Nested Classes?

Summary

Try creating your own classes with both member and static nested classes to see how they help organize and encapsulate your code effectively!

Index

13.2 Anonymous Inner Classes

Java provides a convenient way to declare and instantiate a class all at once without explicitly naming the class: this is called an anonymous inner class. Anonymous inner classes are useful when you need a one-time-use implementation of an interface or subclass, typically for event handling, callbacks, or simple method overrides.

What Are Anonymous Inner Classes?

An anonymous inner class is an unnamed class defined and instantiated in a single expression. You usually use it to provide an implementation of an interface or an abstract class on the spot, without cluttering your code with a separate class declaration.

Syntax Overview

InterfaceOrClassType instance = new InterfaceOrClassType() {
    // Class body: override methods or add new code
};

Typical Use Cases

Before Java 8 introduced lambdas, anonymous inner classes were commonly used for:

Example 1: Using Anonymous Inner Class for Runnable

Suppose you want to create a simple thread task:

public class Demo {
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Task running in a thread");
            }
        };

        Thread thread = new Thread(task);
        thread.start();
    }
}

Here, new Runnable() { ... } defines and creates an unnamed class implementing Runnable and overrides run() right away.

Example 2: Anonymous Inner Class vs Lambda Expression

Since Java 8, lambda expressions provide a more concise way to do the same thing when targeting functional interfaces (interfaces with a single abstract method).

// Using anonymous inner class
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running with anonymous inner class");
    }
};

// Using lambda expression (Java 8+)
Runnable r2 = () -> System.out.println("Running with lambda");

Both create a runnable task, but lambdas are shorter and often clearer.

More Practical Example: Action Listener in GUI

Before lambdas, adding an action listener to a button looked like this:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button clicked!");
    }
});

With Java 8 lambdas, it can be simplified:

button.addActionListener(e -> System.out.println("Button clicked!"));

Key Points About Anonymous Inner Classes

When to Use Anonymous Inner Classes vs Lambdas

Summary

Anonymous inner classes provide a flexible way to create unnamed, one-off implementations for interfaces or classes directly within your code. They were widely used before lambdas arrived in Java 8, especially for GUI event handling and thread tasks. Although lambdas offer a cleaner syntax for many cases, understanding anonymous inner classes is crucial for maintaining and understanding legacy Java code.

Try rewriting some of your anonymous inner classes using lambda expressions where possible to see how they compare in conciseness and clarity!

Index

13.3 Local Inner Classes and Use Cases

In Java, local inner classes are classes defined inside a method, rather than at the class level. These classes are local to the method where they are declared, meaning their scope and visibility are confined to that method. Local inner classes provide a neat way to encapsulate helper functionality that is only relevant within a single method, improving code organization and readability.

What Are Local Inner Classes?

A local inner class is declared within a method (or a block) and cannot be accessed from outside that method. They behave like any other inner class but have some specific rules due to their local scope:

Why Use Local Inner Classes?

Local inner classes are useful when:

Example: Local Inner Class Inside a Method

Here’s a simple example demonstrating a local inner class defined inside a method:

public class Calculator {

    public void calculateSquares(int[] numbers) {
        // Local inner class defined within the method
        class SquarePrinter {
            void printSquares() {
                for (int num : numbers) {
                    System.out.println(num + " squared is " + (num * num));
                }
            }
        }

        // Create an instance and use it
        SquarePrinter printer = new SquarePrinter();
        printer.printSquares();
    }

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        int[] values = {2, 3, 4};
        calc.calculateSquares(values);
    }
}

In this example:

Accessing Variables

Local inner classes can access:

If you try to modify a variable accessed by the local class after declaration, the compiler will issue an error, ensuring safety and predictability.

Use Cases for Local Inner Classes

Some common scenarios where local inner classes shine:

Summary

Local inner classes provide a powerful way to encapsulate class definitions within methods. They promote cleaner code by limiting class visibility and keeping related logic bundled together. Understanding their scope rules and usage helps you write better-organized Java code that is easier to maintain.

Index