foreach-ui logo
codeLanguages
account_treeDSA

Quick Actions

quizlock Random Quiz
trending_uplock Progress
  • 1
  • 2
  • 3
  • 4
  • 5
  • quiz
Java
  • Understand the four types of nested classes in Java
  • Learn when to use static vs non-static nested classes
  • Master anonymous classes for event handling and callbacks

Inner Classes and Nested Classes in Java

Sometimes classes are so tightly coupled that one only makes sense inside another. A car's engine. A linked list's nodes. Java's nested classes let you define one class inside another, creating that tight logical relationship.

Benefits: grouped code, more encapsulation (inner class accesses outer's private members), better readability. But overuse makes code harder to follow.

Types of Nested Classes

Java gives you four types, each with different characteristics:

Type Declared With Has Access To Can Have Static Members
Static Nested Class static Only static members of outer Yes
Inner Class (no modifier) All members of outer No (until Java 16)
Local Class Inside method Outer class + local final/effectively final vars No
Anonymous Class At expression Outer class + local final/effectively final vars No

Static Nested Classes

A static nested class is simply a class defined inside another class with the static keyword. It's associated with the outer class itself, not with any instance.

When to Use Static Nested Classes

Use static nested classes when:

  • The nested class doesn't need access to instance members of the outer class
  • You want to group a helper class with its main class
  • You're implementing a builder pattern or similar design pattern

Defining a Static Nested Class

public class University {
    private String name;
    private static int totalStudents = 0;
    
    public University(String name) {
        this.name = name;
    }
    
    // Static nested class
    public static class Address {
        private String street;
        private String city;
        private String country;
        
        public Address(String street, String city, String country) {
            this.street = street;
            this.city = city;
            this.country = country;
        }
        
        // Can access static members of outer class
        public void printInfo() {
            System.out.println("Total students in all universities: " + totalStudents);
            // Cannot access 'name' - it's an instance variable
        }
    }
}

Creating and Using Static Nested Classes

// Create without an instance of outer class
University.Address address = new University.Address("123 Main St", "Boston", "USA");

// Or if imported
Address address = new University.Address(...);

Key insight: Static nested classes are essentially regular classes that happen to be defined inside another class for organizational purposes. They can be thought of as a way to create a namespace.

The Builder Pattern Example

One of the most common uses of static nested classes is the Builder pattern:

public class Pizza {
    private final String size;        // Required
    private final String crust;       // Required
    private final boolean cheese;     // Optional
    private final boolean pepperoni;  // Optional
    private final boolean mushrooms;  // Optional
    
    // Private constructor - only Builder can create Pizza
    private Pizza(Builder builder) {
        this.size = builder.size;
        this.crust = builder.crust;
        this.cheese = builder.cheese;
        this.pepperoni = builder.pepperoni;
        this.mushrooms = builder.mushrooms;
    }
    
    // Static nested Builder class
    public static class Builder {
        // Required parameters
        private final String size;
        private final String crust;
        
        // Optional parameters - initialized to defaults
        private boolean cheese = false;
        private boolean pepperoni = false;
        private boolean mushrooms = false;
        
        public Builder(String size, String crust) {
            this.size = size;
            this.crust = crust;
        }
        
        public Builder cheese() {
            this.cheese = true;
            return this;
        }
        
        public Builder pepperoni() {
            this.pepperoni = true;
            return this;
        }
        
        public Builder mushrooms() {
            this.mushrooms = true;
            return this;
        }
        
        public Pizza build() {
            return new Pizza(this);
        }
    }
}

// Fluent usage
Pizza pizza = new Pizza.Builder("Large", "Thin")
    .cheese()
    .pepperoni()
    .build();

Inner Classes (Non-Static Nested Classes)

An inner class is a non-static nested class. Each instance of an inner class is associated with an instance of the outer class, giving it access to all members, including private ones.

When to Use Inner Classes

Use inner classes when:

  • The nested class needs access to instance members of the outer class
  • Each instance of the nested class is tied to an instance of the outer class
  • You want to hide a helper class that only the outer class uses

Defining an Inner Class

public class LinkedList<E> {
    private Node head;
    private int size;
    
    // Inner class - each Node belongs to a LinkedList instance
    private class Node {
        E data;
        Node next;
        
        Node(E data) {
            this.data = data;
        }
        
        // Can access outer class members directly
        void printListSize() {
            System.out.println("List has " + size + " elements");
        }
    }
    
    public void add(E element) {
        Node newNode = new Node(element);  // Creates node tied to this list
        // ... add to list
        size++;
    }
    
    // Iterator as inner class - needs access to list's head
    public class ListIterator implements Iterator<E> {
        private Node current = head;
        
        @Override
        public boolean hasNext() {
            return current != null;
        }
        
        @Override
        public E next() {
            E data = current.data;
            current = current.next;
            return data;
        }
    }
}

The Implicit Reference to Outer Class

Every inner class instance holds an implicit reference to its outer class instance:

public class Outer {
    private int value = 10;
    
    public class Inner {
        private int value = 20;
        
        public void printValues() {
            System.out.println(value);        // Inner's value: 20
            System.out.println(this.value);   // Inner's value: 20
            System.out.println(Outer.this.value);  // Outer's value: 10
        }
    }
}

Creating Inner Class Instances

// Must have outer class instance first
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

// Or in one line
Outer.Inner inner = new Outer().new Inner();

Local Classes

A local class is defined inside a method. It's visible only within that method but can access the method's local variables (if they're final or effectively final).

When to Use Local Classes

Use local classes when:

  • You need a class only within a single method
  • The class needs to access local variables
  • The logic is too complex for an anonymous class but still localized

Defining a Local Class

public class Calculator {
    
    public int processNumbers(int[] numbers) {
        // Local class defined inside method
        class Processor {
            private int sum = 0;
            private int count = 0;
            
            void process(int n) {
                sum += n;
                count++;
            }
            
            int getAverage() {
                return count > 0 ? sum / count : 0;
            }
        }
        
        Processor proc = new Processor();
        for (int n : numbers) {
            proc.process(n);
        }
        return proc.getAverage();
    }
}

Accessing Local Variables

Local classes can access local variables, but only if they're effectively final:

public void processWithCallback(int multiplier) {
    // multiplier is effectively final - never reassigned
    
    class Multiplier {
        int apply(int value) {
            return value * multiplier;  // OK - multiplier is effectively final
        }
    }
    
    // This would cause an error:
    // multiplier = 10;  // Now multiplier is not effectively final
}

Anonymous Classes

An anonymous class is a local class without a name. It's defined and instantiated in a single expression, typically when implementing an interface or extending a class for one-time use.

When to Use Anonymous Classes

Use anonymous classes when:

  • You need to implement an interface or extend a class once
  • The implementation is short (1-3 methods)
  • You don't need to reuse the class elsewhere

Anonymous Class Syntax

// Implementing an interface
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running in anonymous class");
    }
};

// Extending a class
Thread thread = new Thread() {
    @Override
    public void run() {
        System.out.println("Custom thread behavior");
    }
};

Common Use Cases

Event listeners:

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

Comparators:

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Anonymous Classes vs Lambda Expressions

For functional interfaces (interfaces with one abstract method), prefer lambdas:

// Anonymous class (verbose)
Runnable task1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Lambda (concise)
Runnable task2 = () -> System.out.println("Hello");

// Anonymous class for Comparator
Comparator<String> comp1 = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};

// Lambda for Comparator
Comparator<String> comp2 = (a, b) -> a.length() - b.length();

Rule of thumb: Use lambdas for functional interfaces, anonymous classes when you need state (fields) or multiple methods.

Choosing the Right Type

Situation Use
Helper class that doesn't need outer instance Static nested class
Class needs access to outer instance's members Inner class
Class needed only in one method, with some complexity Local class
One-time interface implementation, simple logic Anonymous class or lambda
One-time interface implementation, needs state Anonymous class

Static nested classes are for logical grouping without instance dependency. Inner classes get an implicit reference to the outer instance (OuterClass.this). Local and anonymous classes can only access effectively final local variables. Prefer lambdas over anonymous classes for functional interfaces. Don't overuse nesting—if a class makes sense on its own, make it top-level.

© 2026 forEach. All rights reserved.

Privacy Policy•Terms of Service