Non-static inner classes always capture a reference to their enclosing instance. That reference is passed implicitly to the inner’s constructor and stored in a field. If that reference is ever null (via reflection, frameworks, bytecode gen, deserialization, etc.), you get a time-bomb NPE later.
WHAT’S NEW IN JAVA 25
- 🛡️ javac now injects a null-check in constructors of non-static inner classes.
- If the outer reference is null, construction fails immediately, not later.
EXAMPLE
class Outer {
class Inner { /* ... */ }
}
new Outer().new Inner(); // OK
// Forcing a null "outer" (reflection/bytecode hacks) now throws early in JDK 25.
WHY THIS MATTERS
- Earlier failure ⇒ easier debugging & safer invariants.
- Frameworks that craft instances should respect the invariant that the captured outer is non-null.
TEMPORARY ESCAPE HATCH (NOT RECOMMENDED)
- You can disable the injected check with:
javac -XDnullCheckOuterThis=false
Use only as a stop-gap while fixing code paths that smuggle null outers.
BEST PRACTICE
- Prefer static nested classes when you don’t need an outer instance.
- If you do need a non-static inner, never construct it without a real Outer:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
This is one of those “fail fast, fail loud” changes that makes codebases sturdier. 💪
#Java25 #JDK25 #Java #javac #NullSafety #InnerClasses #CleanCode #Refactoring