Back to Anonymous Classes
Q3Question 3 of 6

What does `this` refer to inside an anonymous class, and how do you reference the enclosing instance?

Key Concepts
  • Inside an anonymous class, `this` refers to the anonymous class instance itself, not the enclosing object.
  • To access the enclosing instance, use the qualified form `OuterClass.this`.
  • Shadowed fields and methods require qualification: `this.field` for the anonymous field, `Outer.this.field` for the outer field.
  • Unshadowed outer members can be accessed directly by name without qualification.
  • In a lambda, `this` refers to the enclosing instance because a lambda does not introduce its own `this` scope.
  • In a static context there is no enclosing instance, so `Outer.this` is unavailable and causes a compile error.
  • Capturing `Outer.this` keeps the outer object alive and can cause memory leaks in long-lived listeners.

Definition

In an anonymous class, `this` is the anonymous object; to reach the outer object you write `OuterClass.this`.
  • this in anonymous class – refers to the anonymous class instance, because the anonymous class is a real class with its own object identity.
  • Qualified this – use EnclosingClassName.this to reference the enclosing instance from within the anonymous class body.
  • Shadowing – when both the anonymous class and the enclosing class define the same field or method name, this.name resolves to the anonymous member and Outer.this.name resolves to the outer member.
  • Lambda contrast – inside a lambda, this refers to the enclosing instance directly, because a lambda does not create its own this scope.
  • Static context – in a static method, there is no enclosing instance, so Outer.this is a compile error.

Imagine Rajesh's regular account manager at National Bank of India hires a temporary stand-in clerk for one task. When that stand-in clerk says "I," they mean themselves, the temporary clerk, not the account manager. If the stand-in needs to refer to the manager who hired them, they must say "my manager" explicitly. The stand-in's "I" is always the stand-in. To point at the boss, they name the boss.

Understanding the Concepts

What does this refer to inside an anonymous class?

An anonymous class is a real class with its own instance, so inside its body this naturally denotes that instance. If the anonymous class implements Runnable, then inside run() the expression this is the Runnable object.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class TransferService {
    private String channel = "NEFT";

    public void demonstrateThis() {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                // 'this' is the anonymous Runnable instance
                System.out.println("this class: " + this.getClass().getSimpleName());
                System.out.println("this identity: " + System.identityHashCode(this));
            }
        };
        task.run();
    }

    public static void main(String[] args) {
        new TransferService().demonstrateThis();
    }
}
TEXT
this class: 
this identity: 1234567890

The class name is empty because the anonymous class has no simple name. The identity hash confirms this is the anonymous object, not the TransferService.

How do you reference the enclosing instance?

Java provides the qualified this syntax: OuterClassName.this. If the enclosing class is TransferService, then inside the anonymous class, TransferService.this gives you the enclosing TransferService object.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class TransferService {
    private String channel = "NEFT";

    public void registerInRegistry() {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                // Access enclosing instance via qualified this
                TransferService outer = TransferService.this;
                System.out.println("Outer channel: " + outer.channel);
                System.out.println("Outer identity: " + System.identityHashCode(outer));
                System.out.println("Anon identity:  " + System.identityHashCode(this));
            }
        };
        task.run();
    }

    public static void main(String[] args) {
        new TransferService().registerInRegistry();
    }
}
TEXT
Outer channel: NEFT
Outer identity: 111111111
Anon identity:  222222222

How does field and method shadowing work?

When both the enclosing class and the anonymous class define a member with the same name, this.name resolves to the anonymous member and Outer.this.name resolves to the outer member.

JAVA
package com.bank.service;

public class NotificationService {
    private String channel = "EMAIL";

    public void sendNotification() {
        Runnable task = new Runnable() {
            private String channel = "SMS"; // shadows outer field

            @Override
            public void run() {
                System.out.println("Anonymous channel: " + this.channel);
                System.out.println("Outer channel:     " + NotificationService.this.channel);
            }
        };
        task.run();
    }

    public static void main(String[] args) {
        new NotificationService().sendNotification();
    }
}
TEXT
Anonymous channel: SMS
Outer channel:     EMAIL

Without the qualified form, you could not reach the shadowed outer field.

When can you access outer members without qualification?

If the outer class has a unique field or method that the anonymous class does not redefine, you can access it directly by name. Qualification is only required when there is a naming clash or when you want to pass the outer object itself as a reference.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class AuditService {
    private String auditLog = "AUDIT_LOG_2024";

    public void createTask() {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                // No shadowing: auditLog resolves to the outer field directly
                System.out.println("Using log: " + auditLog);
            }
        };
        task.run();
    }

    public static void main(String[] args) {
        new AuditService().createTask();
    }
}
TEXT
Using log: AUDIT_LOG_2024

How is this different in an anonymous class vs a lambda?

A lambda does not create its own this scope. Inside a lambda, this refers to the enclosing instance directly, exactly as it would in the surrounding method. This is the opposite of an anonymous class, where this is the anonymous object.

| Feature | Anonymous Class | Lambda |
|---|---|---|
| this refers to | The anonymous object | The enclosing instance |
| Access outer this | Outer.this | this (already the outer) |
| Own identity | Has its own object identity | No separate object identity |

JAVA
package com.bank.service;

public class ThisComparison {
    private String name = "TransferService";

    public void compare() {
        // Anonymous class: this = anonymous object
        Runnable anon = new Runnable() {
            @Override
            public void run() {
                // this.name would fail: Runnable has no 'name'
                System.out.println("Anon this:  " + this.getClass().getName());
            }
        };

        // Lambda: this = enclosing ThisComparison instance
        Runnable lambda = () -> {
            System.out.println("Lambda this: " + this.getClass().getName());
            System.out.println("Lambda name: " + this.name);
        };

        anon.run();
        lambda.run();
    }

    public static void main(String[] args) {
        new ThisComparison().compare();
    }
}
TEXT
Anon this:  com.bank.service.ThisComparison$1
Lambda this: com.bank.service.ThisComparison
Lambda name: TransferService

What happens in a static context?

If the anonymous class is created inside a static method, there is no enclosing instance. Using Outer.this is a compile error because only instance methods and instance initializers have an enclosing instance.

JAVA
package com.bank.service;

public class StaticContextDemo {

    public static void main(String[] args) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                // StaticContextDemo.this is NOT available here
                // because main() is static
                System.out.println("No enclosing instance in static context");
            }
        };
        task.run();
    }
}

What are the memory implications of capturing Outer.this?

Capturing Outer.this keeps the outer object alive as long as the anonymous instance lives. In banking systems where event listeners may be registered for the application lifetime, this can cause subtle memory leaks.

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class ListenerLeakDemo {
    private static final List<Runnable> registry = new ArrayList<>();
    private BigDecimal[] largeCache = new BigDecimal[100_000];

    public void registerListener() {
        // The anonymous class holds a reference to this ListenerLeakDemo
        // instance, keeping largeCache alive even if nothing else references it
        registry.add(new Runnable() {
            @Override
            public void run() {
                System.out.println("Listener for: " + ListenerLeakDemo.this);
            }
        });
    }
}

Pankaj, the architect at National Bank of India, recommends using static nested classes or lambdas for long-lived listeners to avoid holding the enclosing instance unnecessarily.

What should a senior engineer know?

The this reference behaviour is a key factor when choosing between an anonymous class and a lambda. If your callback needs its own identity (for example, to register and later unregister itself from a listener registry using == comparison), an anonymous class gives you that distinct this. If you just need to capture the enclosing service's state, a lambda's this already points to the enclosing instance, which is simpler and avoids the qualified form.

JAVA
package com.bank.service;

import java.util.HashSet;
import java.util.Set;

public class RegistryPattern {
    private final Set<Runnable> listeners = new HashSet<>();

    public void addAndRemove() {
        Runnable listener = new Runnable() {
            @Override
            public void run() {
                System.out.println("Processing Priya's account event");
            }
        };

        listeners.add(listener);
        System.out.println("Registered: " + listeners.size());

        // Can remove because we hold the same 'this' reference
        listeners.remove(listener);
        System.out.println("After removal: " + listeners.size());
    }

    public static void main(String[] args) {
        new RegistryPattern().addAndRemove();
    }
}
TEXT
Registered: 1
After removal: 0

Visual Overview

DIAGRAM
┌─────────────────────────────────────────────┐
│   this Reference in Anonymous Class         │
├─────────────────────────────────────────────┤
│                                             │
│  ┌──────────────────────────────┐           │
│  │  Enclosing Class             │           │
│  │  (TransferService)           │           │
│  │  TransferService.this ───────┼──→ outer  │
│  │                              │           │
│  │  ┌────────────────────────┐  │           │
│  │  │  Anonymous Class       │  │           │
│  │  │  this ─────────────────┼──┼──→ anon   │
│  │  │                        │  │           │
│  │  │  this.field        → anon field       │
│  │  │  Outer.this.field  → outer field      │
│  │  └────────────────────────┘  │           │
│  └──────────────────────────────┘           │
├─────────────────────────────────────────────┤
│   this Reference in Lambda                  │
│                                             │
│  ┌──────────────────────────────┐           │
│  │  Enclosing Class             │           │
│  │  (TransferService)           │           │
│  │                              │           │
│  │  Lambda: this ───────────────┼──→ outer  │
│  │  (no separate this scope)    │           │
│  └──────────────────────────────┘           │
└─────────────────────────────────────────────┘

Key Takeaway

Inside an anonymous class, this refers to the anonymous class instance itself, not the enclosing object. The anonymous class is a genuine class with its own instance, so when you write this in its body you get that instance. To reach the enclosing object you use the qualified form, writing the enclosing class name followed by .this, for example TransferService.this.

This matters most when there is shadowing. If the enclosing class and the anonymous class both define a field called channel, then this.channel resolves to the anonymous field and NotificationService.this.channel resolves to the outer field. Without the qualified form you cannot reach the shadowed outer value. The same applies to methods.

The contrast with lambdas is the part interviewers want to hear. A lambda does not create its own this scope, so inside a lambda this already means the enclosing instance, exactly as in the surrounding method. In a bank's service class, the lambda reads this.channel and gets the service's channel directly, with no qualified form needed.

A senior concern is memory. Capturing the outer this keeps the enclosing object alive for as long as the anonymous listener lives. In banking systems where event listeners may be registered for the application lifetime, this can cause subtle leaks. Pankaj recommends using static nested classes or lambdas for long-lived listeners to avoid holding the enclosing instance unnecessarily.

Remember one sentence: in an anonymous class this is the anonymous object and you qualify with Outer.this to reach the enclosing instance, whereas in a lambda this is already the enclosing instance.

---

Share
Use ← → arrow keys to navigate