Explain Codes LogoExplain Codes Logo

Getting hold of the outer class object from the inner class object

java
reflection
encapsulation
best-practices
Nikita BarsukovbyNikita Barsukov·Dec 2, 2024
TLDR

In a non-static inner class, we can easily access the outer class instance with OuterClass.this:

public class Outer { private String data = "It's outer data!"; class Inner { String showOuterData() { // It's not the CIA secret. Here is the data. return Outer.this.data; } } } Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); System.out.println(inner.showOuterData()); // Prints "It's outer data!"

Here, Outer.this within the Inner class directly points to the Outer instance that houses it, no GPS needed.

Inner class hack: Including a method for the outer instance

When you've got an inner class to deal with and need a road back to the outer class, OuterClass.this presents a direct and efficient solution. But there are other ways to handle this if the standard route isn't viable.

Modify the Inner class: The straight-forward approach

class Outer { class Inner { public Outer getOuterInstance() { return Outer.this; } // Border security: "Who won the World Series in 2020?" // getOuterInstance: "Los Angeles Dodgers." // Border security: "You may pass." } }

The hidden door: Accessing the implicit field

The compiler creates an implicit field named this$0 to keep a reference to the outer instance. This hidden door, though package-private, can still be used through reflection:

import java.lang.reflect.Field; class Outer { class Inner { } public static void main(String[] args) throws Exception { Outer outer = new Outer(); Inner inner = outer.new Inner(); Field f = inner.getClass().getDeclaredField("this$0"); // Picking the door lock... f.setAccessible(true); Outer outerReference = (Outer) f.get(inner); System.out.println(outerReference); // We are in! } }

Reflection with care: Handle with kid gloves

While reflection does give us the power to access this$0, making it accessible is much akin to playing with fire. It can raise security vulnerabilities, and future Java updates might just extinguish this trick.

The shadow knows: Using shadowed variables for outer access

Using shadowed variables allows us to reference outer instance variables that have the same names as variables in the inner class. Think of it as the Clark Kent of variables – looks ordinary but has special access:

class Outer { String name = "I'm Batman"; class Inner { String name = "No, I'm Batman"; void printNames() { System.out.println(name); // Prints "No, I'm Batman" System.out.println(Outer.this.name); // Prints "I'm Batman" // Inner class: "You're Batman too? We should start a club." } } }

What makes "Outer.Class.this" the preferred method?

  • Encapsulation Mission: Using OuterClass.this helps your code maintain Java's encapsulation principles, giving a clear relationship between the classes, while reflection may seem like a James Bond mission that could fail.
  • Compile-Time Sidekick: Reflection lacks compile-time checking, but OuterClass.this has this superpower and brings clarity to code.
  • Speedy Gonzales: Reflection causes a performance penalty as it's at runtime. It's faster to use OuterClass.this because it's treated at compile time.