Inner Classes in Java

Naveen AnamaneniUpdated · 5 min read

A car has an engine, a dashboard and an alarm. The engine is a separate part that could be fitted to any car; the dashboard only makes sense attached to this car, because it shows this car's speed. Java lets you express that difference by declaring classes inside other classes. These nested classes keep helper types close to where they're used, and some kinds can read the outer object's private fields directly.

The Four Kinds

Four places a class can live inside another
Car.java▶ Try it
public class Car {
    interface Alarm { String ring(); }
 
    static class Engine {
        final int cc;
        Engine(int cc) { this.cc = cc; }
    }
 
    class Dashboard {
        String show() {
            return owner + " is driving at " + speed + " km/h";
        }
    }
 
    private final String owner;
    private final Engine engine;
    private int speed;
 
    Car(String owner, Engine engine) {
        this.owner = owner;
        this.engine = engine;
    }
 
    void accelerate(int by) { speed += by; }
 
    String check() {
        int limit = 80;
        class SpeedCheck {
            String verdict() {
                return speed > limit ? "over the limit" : "within the limit";
            }
        }
        return new SpeedCheck().verdict();
    }
 
    public static void main(String[] args) {
        Car car = new Car("Navika", new Engine(1197));
        car.accelerate(60);
        Car.Dashboard dash = car.new Dashboard();
        System.out.println(dash.show());
        System.out.println("Engine: " + car.engine.cc + " cc");
 
        Alarm alarm = new Alarm() {
            public String ring() { return "Beep! Door open"; }
        };
        System.out.println(alarm.ring());
 
        car.accelerate(30);
        System.out.println(dash.show() + ", " + car.check());
    }
}
Output
Navika is driving at 60 km/h
Engine: 1197 cc
Beep! Door open
Navika is driving at 90 km/h, over the limit

How it works

  1. static class Engine is a static nested class: it has no link to any Car object, so new Engine(1197) works on its own.
  2. class Dashboard is a member inner class: each dashboard belongs to one car, created with car.new Dashboard(), and reads that car's private owner and speed directly. After the car speeds up, the same dashboard shows 90.
  3. class SpeedCheck inside check() is a local class: it can use the method's local variable limit.
  4. new Alarm() { ... } is an anonymous class: a one-off implementation of Alarm, written as an expression with no class name.

Choosing the Right Kind

KindDeclaredNeeds an outer object?Can read outer instance fields?Typical use
Static nestedIn the class body, with staticNoNo (only static ones)Helpers and builders: Map.Entry, Car.Engine
Member innerIn the class bodyYesYesViews of the outer object: iterators, dashboards
LocalInside a methodYes, if the method is an instance methodYes, plus effectively final localsA helper used in one method only
AnonymousInside an expressionSame as localSame as localOne-off implementations of an interface or class

Common Mistakes

Making a nested class inner when it doesn't need the outer object. Add static.

Creating an inner class with new Dashboard() from main. It needs an outer object: car.new Dashboard().

Deeply nested classes. One level is usually enough; move bigger classes to their own files.

Under the Hood

The JVM has no idea of nesting: the compiler turns each nested class into a separate class file:

Files after javac -d o Car.java
Car$1.class            anonymous class (numbered)
Car$1SpeedCheck.class  local class (number + name)
Car$Alarm.class        nested interface
Car$Dashboard.class    member inner class
Car$Engine.class       static nested class
Car.class

javap -p shows the trick that lets an inner class reach the outer object: a hidden field holding the outer Car, plus a captured copy of limit for the local class:

javap -p Car$Dashboard and Car$1SpeedCheck (trimmed)
class Car$Dashboard {
  final Car this$0;
  Car$Dashboard(Car);
  java.lang.String show();
}
class Car$1SpeedCheck {
  final int val$limit;
  final Car this$0;
  java.lang.String verdict();
}

That hidden this$0 reference is why an inner object keeps its outer object alive, and why static nested classes are lighter.

Interview Corner

What are the types of nested classes in Java?

Static nested classes, declared static in a class body, have no reference to an outer instance. Inner classes are non-static and come in three kinds: member inner classes declared in the class body, local classes declared inside a method or block, and anonymous classes declared and instantiated in one expression. Inner classes are tied to an instance of the outer class and can access its members, including private ones.

What class files does the compiler create for nested classes?

One per nested class: Outer$Nested.class for static nested and member inner classes, Outer$1.class, Outer$2.class and so on for anonymous classes, and Outer$1Local.class for local classes.

Practice

  1. Easy: Add a static nested class Tyre with a pressure field to Car, and create four tyres in main.
  2. Harder: Add a member inner class TripMeter to Car that records the speed each time accelerate is called and prints the average speed.

Quick check: Inner classes

4 questions
1Which nested class has no link to an outer object?
2How do you create a member inner Dashboard for car?
3Which kind has no name?
4What file does class Dashboard inside Car compile to?

Key Takeaways

Was this tutorial helpful?

Lesson 168 of 482