Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Java › Classes and objects

Inner classes and anonymous classes

How is a class declared inside a class connected to the outer object?

You can declare a class inside another class. An object of an inner class, which is declared without static, holds a reference to the outer object that created it, so it cannot be created without an outer object. An anonymous class, which is created on the spot without a name, and a lambda expression can both use the local variables around them, but changing the value of such a variable is a compile-time error, and what this refers to inside them is different for the two.

Checked against real execution · The code of the 24 combinations was compiled and run as it is on JDK 21 and checked against the results

Concepts in this lab

Inner classes and nested classes
These are classes declared inside another class. One declared without static is called an inner class (a non-static nested class), and one declared with static is called a static nested class. An object of an inner class is created in association with one outer object and holds a reference to that outer object, so it can use the fields and methods of the outer object directly. An object of a static nested class can be created without an outer object, and it cannot simply use the instance variables of an outer object. While an object of an inner class remains, that reference can also keep the outer object from being removed from memory, so it is better to add static to a class that does not use the outer object.
Anonymous class
This is a class that is given no name and from which one object is created right where the class is declared. You write its body in braces after the name of an interface or a class, as in new Runnable() { ... }, and the result is an object that implements that interface or extends that class. Its advantage is that you do not have to create a separate file for a small implementation that is used only once. Inside an anonymous class, this refers to that newly created object, which is different from a lambda expression, where this refers to the outer object. When the code is compiled, a separate class file is produced whose name is the name of the outer class followed by a number, such as Outer$1.
Effectively final
This describes a local variable that is not declared final but is never assigned again after it is given its first value. Of the local variables of the method that encloses them, an anonymous class and a lambda expression can use only those that are final or effectively final. This is because they take a copy of the value, not the variable itself, so if the original could be changed, it would differ from the copy and cause confusion. Even one line that assigns to the variable again causes a compile-time error. Changing the contents of the array or object that the variable refers to is not assigning to the variable again, so it is allowed.
Lambda expression
This is a syntax, introduced in Java 8, for writing a function with no name in a compact way. It has the form (parameters) -> body, and it is used as the implementation of a functional interface, which has only one abstract method. It lets you pass behavior (code) to another method as if it were a value.
static
This keyword marks a member as belonging to the class, not to an instance. A static field exists only once per class and is shared by all instances, and a static method is called without creating an object. A static field whose value changes is effectively a global variable, so it easily causes problems when several threads access it at the same time.

Try these

Running with the default settings

  1. The compiler reads the Outer class: Inner has no static. An object of an inner class without static is always created in association with one particular Outer object.
  2. new Outer.Inner(): This is a compile-time error. main is a static method, so there is no Outer object at this point, and the code does not specify one either. The compiler rejects the code because it cannot determine which outer object the inner object belongs to.
  3. Error message: "an enclosing instance that contains Outer.Inner is required". It means that there must be an outer object that contains Outer.Inner. Compilation failed, so not a single line of the program runs.

Result of this run

Code

class Outer {
    String name = "A";

    class Inner {                      // inner class (no static)
        String who() { return "inside"; }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.Inner in = new Outer.Inner();   // created without an outer object
        System.out.println(in.who());
    }
}

Assumptions of this model

The code of the 24 combinations (10 distinct cases when the hidden settings are excluded) was compiled and run as it is on JDK 21 on this PC, and whether compilation succeeded, the error messages, and the output were checked against the results. That an object of an inner class refers to the outer object, and that a static nested class has no such reference, was confirmed by looking at the hidden field with reflection. That an object of an anonymous class and a lambda expression keep a copy of the value of a local variable (the reference, for an array) was also confirmed through hidden fields on this JDK. However, the form in which a lambda expression keeps the value can differ depending on the JVM implementation. The wording of the error messages is that of javac in JDK 21 and can differ slightly by version. javac in JDK 21 does not create the hidden reference field when the class inside does not use the outer object at all (the anonymous class in this lab is such a case). Even so, the rule that an outer object is required to create an inner class without static is the same.

Previous lab: Access modifiers and encapsulation · Next lab: Immutability and defensive copies

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these