00:00

Member Inner Class in Java – Syntax, Rules and Examples

A member inner class is a non-static class declared directly inside another class, at the same level as fields and methods. Every inner class object is bound to an object of the outer class and can freely use that outer object's fields and methods, including private ones.

This tutorial shows how to declare and instantiate member inner classes, how they access the outer object, how to resolve name clashes with Outer.this, what the compiler generates behind the scenes, and when a static nested class is the better choice.

1. Syntax

Syntax
class Outer {
    [access modifier] class Inner {      // no 'static' keyword
        // fields, constructors, methods
    }
}

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // inner object bound to 'outer'

Like other members, an inner class can be public, protected, package-private or private, and can also be final or abstract.

2. Example: Students of a University

MemberInnerBasics.java
public class MemberInnerBasics {
    static class University {
        private final String name;
        private int enrolled = 0;

        University(String name) { this.name = name; }

        // Member inner class: each Student belongs to one University object
        class Student {
            private final String studentName;
            private final int rollNo;

            Student(String studentName) {
                this.studentName = studentName;
                this.rollNo = ++enrolled;            // updates the OUTER object's field
            }
            String card() { return studentName + " | roll " + rollNo + " | " + name; }
        }

        Student admit(String who) { return new Student(who); }   // inside: plain 'new'
    }

    public static void main(String[] args) {
        University iit = new University("IIT Delhi");
        University nit = new University("NIT Trichy");

        University.Student a = iit.new Student("Asha");      // outside: outer.new Inner()
        University.Student r = iit.admit("Ravi");
        University.Student m = nit.new Student("Meera");

        System.out.println(a.card());
        System.out.println(r.card());
        System.out.println(m.card());
    }
}
Output
Asha | roll 1 | IIT Delhi
Ravi | roll 2 | IIT Delhi
Meera | roll 1 | NIT Trichy
  • Each Student reads its university's name and increments its enrolled counter directly.
  • Students of different universities see different outer objects, so roll numbers restart for NIT.
  • Outside the outer class, write outerObject.new Inner(); inside an instance method, plain new Inner() uses this as the outer object.

3. A Classic Use: Iterators

The JDK's collections implement iterators as inner classes, because an iterator needs direct access to its collection's internal data. A private inner class keeps the implementation hidden:

IteratorExample.java
import java.util.Iterator;
import java.util.NoSuchElementException;

public class IteratorExample {
    // Classic use: an iterator is an inner class that walks the outer object's data
    static class Playlist implements Iterable<String> {
        private final String[] songs;
        Playlist(String... songs) { this.songs = songs; }

        @Override public Iterator<String> iterator() { return new SongIterator(); }

        private class SongIterator implements Iterator<String> {   // private: hidden from users
            private int index = 0;
            public boolean hasNext() { return index < songs.length; }   // reads outer's array
            public String next() {
                if (!hasNext()) throw new NoSuchElementException();
                return songs[index++];
            }
        }
    }

    public static void main(String[] args) {
        Playlist p = new Playlist("Kesariya", "Tum Hi Ho", "Naatu Naatu");
        for (String song : p) System.out.println("Playing: " + song);
    }
}
Output
Playing: Kesariya
Playing: Tum Hi Ho
Playing: Naatu Naatu

4. Name Clashes and Outer.this

When the inner class, the outer class and a parameter use the same name, this refers to the inner object and OuterClassName.this to the enclosing object:

ShadowingOuterThis.java
public class ShadowingOuterThis {
    private int value = 10;

    class Inner {
        private int value = 20;

        void show(int value) {
            System.out.println("parameter value:        " + value);
            System.out.println("this.value (inner):     " + this.value);
            System.out.println("Outer.this.value:       " + ShadowingOuterThis.this.value);
        }
    }

    public static void main(String[] args) {
        new ShadowingOuterThis().new Inner().show(30);
    }
}
Output
parameter value:        30
this.value (inner):     20
Outer.this.value:       10

5. What the Compiler Generates

An inner class compiles to a separate file, Outer$Inner.class. The compiler adds a hidden final field (this$0) holding the outer object, and an extra constructor parameter to pass it in:

javap-Student.txt
$ javap -p "MemberInnerBasics$University$Student"
Compiled from "MemberInnerBasics.java"
class MemberInnerBasics$University$Student {
  private final java.lang.String studentName;
  private final int rollNo;
  final MemberInnerBasics$University this$0;
  MemberInnerBasics$University$Student(MemberInnerBasics$University, java.lang.String);
  java.lang.String card();
}

That hidden reference is how inner code reaches outer fields, and it is also why an inner object keeps its outer object alive in memory.

6. Static Members in Inner Classes (Java 16+)

Before Java 16, inner classes could not declare static members other than compile-time constants. Since Java 16 (with records), they can:

InnerStaticJava16.java
public class InnerStaticJava16 {
    class Inner {
        static int instances = 0;                 // allowed since Java 16
        static String describe() { return "static method in an inner class"; }
        Inner() { instances++; }
    }

    public static void main(String[] args) {
        InnerStaticJava16 outer = new InnerStaticJava16();
        outer.new Inner();
        outer.new Inner();
        System.out.println(Inner.describe() + ", instances = " + Inner.instances);
    }
}
Output
static method in an inner class, instances = 2

7. Member Inner Class vs Static Nested Class

AspectMember inner classStatic nested class
Declared withNo staticstatic
Outer instanceRequired; reference stored in each objectNot needed
Access to outer instance membersDirectOnly through an explicit reference
Creation from outsideouter.new Inner()new Outer.Nested()
MemoryExtra reference; may keep outer object aliveNo hidden reference

Use a member inner class only when the inner object genuinely needs its outer object. Otherwise, make it static. See Static Nested Class.

8. Summary

  • A member inner class is a non-static class declared as a member of another class.
  • Each instance is bound to an outer instance and can use its members, including private ones.
  • Create it with outer.new Inner(); refer to the outer object with Outer.this.
  • The compiler stores the outer reference in a hidden field, which can cause memory leaks if the inner object outlives the outer one.
  • Prefer a static nested class when the outer instance is not needed.

Related Tutorials