00:00

Local Inner Class in Java – Classes Inside Methods

A local inner class (or simply local class) is a class declared inside a method, constructor or block. Like a local variable, it is visible only within that block. It can use the enclosing method's parameters and local variables, provided they are final or effectively final, and, when declared in an instance method, the members of the outer object.

This tutorial shows how to write local classes, the effectively-final rule, local records and interfaces (Java 16+), and how local classes compare with anonymous classes and lambdas.

1. Syntax

Syntax
returnType method(params) {
    class LocalClass {          // no access modifier, no static
        // fields, constructors, methods
    }
    LocalClass obj = new LocalClass();   // usable only after its declaration, in this block
}

2. Example: Invoice Lines

The Line class is needed only while building one invoice, so it is declared inside the method:

LocalClassBasics.java
import java.util.ArrayList;
import java.util.List;

public class LocalClassBasics {
    private final String currency = "INR";

    String invoice(String customer, double[] amounts) {
        double taxRate = 0.18;                       // effectively final local variable

        // Local class: visible only inside this method
        class Line {
            final double net;
            Line(double net) { this.net = net; }
            double gross() { return net * (1 + taxRate); }             // captures local variable
            String format() { return String.format("%10.2f %s", gross(), currency); }  // outer field
        }

        List<Line> lines = new ArrayList<>();
        for (double a : amounts) lines.add(new Line(a));               // several instances

        StringBuilder sb = new StringBuilder("Invoice for " + customer + "\n");
        double total = 0;
        for (Line l : lines) { sb.append(l.format()).append('\n'); total += l.gross(); }
        return sb.append(String.format("Total: %.2f %s", total, currency)).toString();
    }

    public static void main(String[] args) {
        System.out.println(new LocalClassBasics().invoice("Asha", new double[] {1000, 250.5, 99}));
    }
}
Output
Invoice for Asha
   1180.00 INR
    295.59 INR
    116.82 INR
Total: 1592.41 INR
  • Line reads the local variable taxRate and the outer field currency.
  • Unlike an anonymous class, a local class has a name, so you can create several instances and use it as a type (List<Line>).
  • No code outside invoice() can see or use Line.

3. The Effectively Final Rule

A local class may use the enclosing method's local variables and parameters only if they are never reassigned after initialisation:

EffectivelyFinalRule.java
public class EffectivelyFinalRule {
    public static void main(String[] args) {
        int count = 3;               // effectively final: never reassigned
        String label = "item";

        class Printer {
            void print() {
                for (int i = 1; i <= count; i++) System.out.println(label + " " + i);
            }
        }
        new Printer().print();

        // count++;   // uncommenting this makes 'count' not effectively final:
                      // compile error "local variables referenced from an inner class
                      // must be final or effectively final"
    }
}
Output
item 1
item 2
item 3

The local class captures a copy of each variable's value when an instance is created. Requiring the variable to be unchanging guarantees that the copy and the original never disagree. Fields of the outer object, by contrast, are accessed through the outer reference and can change freely.

4. Local Records, Enums and Interfaces (Java 16+)

Since Java 16 you can also declare records, enums and interfaces locally. They are implicitly static, so they cannot capture local variables or the outer instance:

LocalClassValidation.java
import java.util.List;

public class LocalClassValidation {
    static List<String> validate(String username, String password) {
        int minLength = 8;

        // A named local class used for two checks in this method only
        record Rule(String name, boolean passed) {
            String report() { return (passed ? "PASS " : "FAIL ") + name; }   // local record (Java 16)
        }

        interface Check { boolean test(String s); }                          // local interface (Java 16)
        Check hasDigit = s -> s.chars().anyMatch(Character::isDigit);

        return List.of(
                new Rule("username not blank", !username.isBlank()),
                new Rule("password length >= " + minLength, password.length() >= minLength),
                new Rule("password has a digit", hasDigit.test(password))
        ).stream().map(Rule::report).toList();
    }

    public static void main(String[] args) {
        validate("asha", "secret").forEach(System.out::println);
        System.out.println("--");
        validate("ravi", "secret123").forEach(System.out::println);
    }
}
Output
PASS username not blank
FAIL password length >= 8
FAIL password has a digit
--
PASS username not blank
PASS password length >= 8
PASS password has a digit

Local records are handy for intermediate results, for example in stream pipelines, without polluting the class with a type nobody else needs.

5. Rules of Local Classes

  • No access modifiers (public, private…) and no static; they may be final or abstract.
  • Visible only in the block where they are declared, and only after the declaration.
  • Can access enclosing local variables and parameters only if final or effectively final.
  • In an instance method, can access all outer members; in a static method, only outer static members.
  • Since Java 16 they can declare static members.
  • Compiled to files such as Outer$1Line.class.

6. Local Class vs Anonymous Class vs Lambda

AspectLocal classAnonymous classLambda
Has a nameYesNoNo
Multiple instances / use as a typeYesOne expressionOne expression
Constructors and several methodsYesNo constructors; methods yesOne method only
Can extend a classYesYesNo (functional interfaces only)
Best forNon-trivial helper used several times in one methodOne-off implementation with state or several methodsSingle-method behaviour

7. When to Use a Local Class

  • A helper type is needed only inside one method and would clutter the class if declared as a member.
  • You need more than an anonymous class offers: a constructor, multiple instances or a name to refer to.
  • Local records are ideal for grouping temporary values.

If the class grows large or is needed elsewhere, promote it to a private static nested class.

8. Summary

  • A local class is declared inside a method or block and visible only there.
  • It can use effectively final locals and, in instance methods, the outer object's members.
  • It has a name, so it can be instantiated many times, unlike an anonymous class.
  • Java 16 added local records, enums and interfaces, which are implicitly static.

Related Tutorials