You can't create inner member (non-static
) classes within a call to another constructor. From JLS §8.8.7.1:
An explicit constructor invocation statement in a constructor body (sic: the call to this()
) may not refer to any instance variables or instance methods or inner classes declared in this class or any superclass, or use this
or super
in any expression; otherwise, a compile-time error occurs.
The reason is that non-static
inner classes may require access to the class while it's being constructed. For example:
public class OuterClass {
private String name;
private InnerClass inner;
public OuterClass(String name, InnerClass inner) {
this.name = name;
this.inner = inner;
}
public OuterClass(String name) {
this(name, new InnerClass()); // Will not compile
}
public class InnerClass {
public InnerClass() {
// Access to name is allowed since this inner class isn't static
System.out.println(name);
}
}
}
The major problem here is that when we construct the non-static
inner class, it can access non-static
members from its enclosing instance (the OuterClass
, but the enclosing OuterClass
hasn't yet made its call to super()
and is therefore considered unsafe to access. In fact, if that code was allowed to compile, it would print null
due to constructor invocation order. In short, the InnerClass
would be created before the call to this.name = name
, a similar concept to the information presented in this question.
The solution is to make InnerClass
a static
inner class and pass the name to it directly:
public class OuterClass {
private String name;
private InnerClass inner;
public OuterClass(String name, InnerClass inner) {
this.name = name;
this.inner = inner;
}
public OuterClass(String name) {
this(name, new InnerClass(name));
}
public static class InnerClass {
public InnerClass(String name) {
System.out.println(name);
}
}
}
InnerClass
cannot access name
from the OuterClass
once it's declared static
, so we have to pass it explicitly on construction now, but this is better since the initial code would have been broken anyway.
Edit:
Per your question:
What confused me is that I can create an object of a Date
type in the Person
's constructor, as long as it is not inside the this()
method. I can do this: Date dt = new Date(birthMonth, birthDay, birthYear);
What is the difference between the above and this(...., new Date(birthMonth, birthDay, birthYear), ...)
?
The difference is that in the call outside of this()
, all the calls to super()
have taken place, they take place as part of this()
because of implicit calls to super()
, so the object has reached a point where it is considered okay to be accessed. Your Date
instance can't access the Person
class because there is no context for the Person
and its fields yet, so the compiler doesn't allow it.
In short, once this()
has been called, then at least the calls to super()
have happened, which is the driving force behind this constraint, and also why overridable method calls are discouraged. If a method is overridden by a subclass and then called in the superclass' constructor, fields from the subclass can be accessed before the subclass has even been initialized, even resulting in null
being returned for final
fields. Basically, it's all about protecting yourself from accessing your class before a call to super()
has been invoked.