I'm having a hard time wrapping my head around non-static nested classes in Java. Consider the following example, which prints "Inner" and then "Child".
class Outer {
class Inner {
Inner() { System.out.println("Inner"); }
}
}
public class Child extends Outer.Inner {
Child(Outer o) {
o.super();
System.out.println("Child");
}
public static void main(String args[]) {
new Child(new Outer());
}
}
I understand that instances of Inner always have to be associated with an Outer instance, and that that applies to Child too since it extends Inner. My question is what the o.super() syntax means - why does it call the Inner constructor?
I've only seen a plain super(args) used to call the superclass constructor and super.method() to call the superclass version of an overridden method, but never something of the form instance.super().
It's called a "qualified superclass constructor invocation".
Citing from here:
Explicit constructor invocation statements can be divided into two kinds:
Alternate constructor invocations begin with the keyword this (possibly prefaced with explicit type arguments). They are used to invoke an alternate constructor of the same class.
Superclass constructor invocations begin with either the keyword super (possibly prefaced with explicit type arguments) or a Primary expression. They are used to invoke a constructor of the direct superclass. Superclass constructor invocations may be further subdivided:
Unqualified superclass constructor invocations begin with the keyword super (possibly prefaced with explicit type arguments).
Qualified superclass constructor invocations begin with a Primary expression . They allow a subclass constructor to explicitly specify the newly created object's immediately enclosing instance with respect to the direct superclass (§8.1.3). This may be necessary when the superclass is an inner class.
Inner Classes (non-static child classes) are essentially Nested Classes (static child classes) with implicit links back to their parent objects. Here is your above code, written instead using a static nested class:
class Outer {
static class Inner {
final Outer outer;
Inner(Outer outer) {
this.outer = outer;
System.out.println("Inner");
}
}
}
public class Child extends Outer.Inner {
Child(Outer o) {
super(o); // o.super();
System.out.println("Child");
}
public static void main(String args[]) {
new Child(new Outer());
}
}
Looking at this, you should be able to understand what o.super() was doing.
Why does o.super() in Child ends up invoking Outer.Inner constructor? It's simple: because Child extends Outer.Inner, and constructor calls are always chained up the hierarchy.
Here's a slight expansion to your snippet to illustrate:
class Outer {
Outer() {
System.out.println("Outer");
}
void outerMethod() { }
class Inner {
Inner() {
System.out.println("OuterInner");
outerMethod();
}
String wealth;
}
}
class OuterChild extends Outer {
OuterChild() {
System.out.println("OuterChild");
}
}
public class OuterInnerChild extends Outer.Inner {
OuterInnerChild(Outer o) {
o.super();
System.out.println("OuterInnerChild");
this.wealth = "ONE MILLION DOLLAR!!!";
}
public static void main(String args[]) {
System.out.println(new OuterInnerChild(new Outer()).wealth);
new OuterChild();
}
}
This prints:
Outer
OuterInner
OuterInnerChild
ONE MILLION DOLLAR!!!
Outer
OuterChild
Some key observations:
Because OuterInnerChild extends Outer.Inner, it inherits wealth, just like normal subclass semantics
And just like normal subclass semantics, the constructor of OuterInnerChild chains to the constructor of Outer.Inner
Because OuterChild extends Outer, its constructor chains, even when not invoked explicitly
Whether implicitly or explicitly, the constructor chains up the hierarchy
But why does the compiler demand that OuterInnerChild constructor takes an Outer o, and that o.super() is invoked?
Now that is specific to inner class semantics: it's done to ensure that all instances of OuterInnerChild has an enclosing Outer instance for Outer.Inner, the super class of OuterInnerChild. Otherwise, the constructor of Outer.Inner would not have an enclosing instance of Outer to invoke outerMethod() on.
Conceptually, a non-static inner class “belongs” to a particular object. It's sorta like each one gets its own version of the class, much like a non-static field or method belongs to a particular object.
So that's why we have funny syntax like instance.new Inner() and instance.super() — for contexts where the answer to the question “but whose Inner?” isn't immediately obvious. (In a non-static method of the outer class, you can just say new Inner(), and as usual that's short for this.new Inner().)
Always not to forget basic principles, in process of calling a sub class constructor it's always the parent class is instantiated first irrespective of inner/outer classes. In your scenario, as you are extending inner class and your inner class is a member of parent class which needs to be instantiated then followed by calling the actual inner class constructor.
Related
So I have this code:
public class A {
private State _state;
public abstract class State {
protected void setState(State newState) {
_state = newState;
}
}
public A() {
_state = new AbstractState(this);
}
}
public abstract class AbstractState extends A.State {
AbstractState(A a) {
a.super();
}
}
And I'm having a hard time understanding how it works.
So when a A object is created it creates a AbstractState object with that same A object as a parameter. And the AbstractState constructor, which receives that A object, calls a.super().
From my understanding, a.super() calls the construtor of the super class of A a, which is Object, right? So what's the point?
I know that AbstractState is an abstract class. A subclass could be:
public class StateOne extends AbstractState {
StateOne(A a) {
super(a);
}
}
The question remains though.
From my understanding, a.super() calls the constructor of the super class of A a, which is Object, right? So what's the point?
I think you are asking why you have to write a.super() rather than super().
First of all, it is not calling the constructor of A or of the superclass of A. The super(...) call in a constructor always means a constructor in the immediate superclass of the class that you are instantiating. In this case the superclass is the inner class A.State.
What a.super() actually says is "call my super class constructor with a as my enclosing instance".
The unusual syntax is needed because the superclass is an inner class, and when an inner class is instantiated, it needs to know what instance of the enclosing class that it is inside.
In your example, State is declared as an inner class (i.e. not static) so the setState method can see the private _state variable of some instance the enclosing class (A). The a.super() is necessary so that the JVM can know which instance of A is the enclosing one.
To call an inner class (State), it must tell it who is its super class A first. JVM can find who is the father class of StateOne, just using this statement " extends A.State" .
a.super() is corresponding to A.State
a --> A in " extends A.State ", tell inner class who is your father class (the instance of father class)
super() ----> State in "extends A.State ", it is the constructor of inner class
You can understand it like this, a common class extends use "super()/super(xx)", but extending inner class must have to know an instance of its super class first. So how to know its super class, in the constructor, use the parameter. Then, using super() calls father's constructor, like this, inner class use (my father class).super()
If we have a code...
public class Hello
{
public static void main(String args[])
{
Outer obj=new Outer();
obj.method1();
}
}
class Outer
{
void method1()
{
class Inner
{
}
}
}
I wanted to know,when the Inner class will be loaded by the ClassLoader.
Is it loaded at the time the method1() is called,or at the time when we will be creating its instance?And the class Inner is not performing any operation in method1(),its an empty class.Also,I wanted to know,how to create an instance of inner class in the above example?
Class Outer.Inner will be loaded the first time another class that refers to it as a variable type, method parameter type, method return type, superclass type, type parameter bound, or target type of an initializer or static method, or host class of a static variable reference is loaded. In your example, I would expect it never to be loaded.
Also,I wanted to know,how to create an instance of inner class in the above example?
As it is written, class Outer.Inner is accessible only inside method Outer.method1(), therefore it can be instantiated only within that method. There, you can just use new Inner(). If you want it to be instantiable from elsewhere then move its declaration out of the method body:
class Outer
{
class Inner
{
}
void method1()
{
}
}
That's better form for a named inner class anyway. It will not change when or whether Outer.Inner is loaded.
With that change, you can instantiate Outer.Inner anywhere within a constructor or instance method of Outer via the form new Inner(). If, however, you want to instantiate one from a different class, or in a static method of class Outer, then it's a bit trickier. The important thing to realize is that each instance of Outer.Inner needs an associated instance of Outer. This is determined from context when the instantiation is performed in an instance method of Outer, but if it is performed without such a context then the syntax is:
public static void main(String args[])
{
Outer obj=new Outer();
Outer.Inner inner = obj.new Outer.Inner();
}
Quoting from Section 12.4.1 of the JLS :
A class or interface type T will be initialized immediately before the
first occurrence of any one of the following:
T is a class and an instance of T is created.
A static method declared by T is invoked.
A static field declared by T is assigned.
A static field declared by T is used and the field is not a constant
variable (§4.12.4).
T is a top level class (§7.6) and an assert statement (§14.10)
lexically nested within T (§8.1.3) is executed.
Therefore, a class (regardless of whether is a nested class) will follow the same rules. In your particular case, Inner will not be loaded until an instance of the class is created.
To answer your second question, you can only create an instance of Inner inside method1 since Inner is a method local class whose scope is limited to method1 :
void method1()
{
class Inner
{
public Inner()
{
System.out.println("inner()");
}
}
Inner inner = new Inner();
}
To achieve the inner class, you need object anyway. If you call method1() like this, inner class never be called.
But if you define some object in method1() or public Outer() constructor, you can get result from inner class.
In the book Java: The complete reference
// Demonstrate when constructors are called.
// Create a super class.
class A {
A() {
System.out.println("Inside A's constructor.");
}
}
// Create a subclass by extending class A.
class B extends A {
B() {
System.out.println("Inside B's constructor.");
}
}
// Create another subclass by extending B.
class C extends B {
C() {
System.out.println("Inside C's constructor.");
}
}
class CallingCons {
public static void main(String args[]) {
C c = new C();
}
}
Output:
Inside A’s constructor
Inside B’s constructor
Inside C’s constructor
It is demonstrating how the constructor of a subclass is called. But why are constructors of the super class called in the absence of a super() constructor.
Why did the Java Language designers consider it necessary to do so?
As others have pointed out, if you don't start your constructor with a super(...) call, the compiler will put in a call to super() for you.
As to the why, you have to start with remembering what a constructor is for in the first place: initializing the object. What does that mean, specifically? In practice, it means assigning values to the object's fields, and establishing invariants.
Without a call to super(), the B and A classes wouldn't have a chance to do that for whatever fields they contain. And you can't even have the C() constructor do it for them, if those fields are private, since private fields aren't accessible outside your class (not even your super class's fields are accessible). Even if you could, it wouldn't be a good idea; it would also break encapsulation. For instance, imagine having to change your code if a super class -- possibly a complex one whose internals you're not an expert in -- suddenly decided to change its implementation details.
To illustrate this, consider a very simple set of classes:
public class Super {
private final String name;
Super() {
name = "default";
}
public String name() {
return name.toUpperCase();
}
}
public class Sub extends Super {
public Sub() {
// don't do anything
}
}
When you instantiate Sub, it will start out by calling Super's constructor. If it didn't, the name field would be null (the default value for reference types). But the name() method doesn't check for null; it assumes that the reference is non-null, because the constructor establishes that invariant. So, in our pseudo-Java that doesn't call the super constructor, Super.name has to get a bit more complicated -- it has to check for name == null.
You can imagine that as the classes gain more fields, with more interesting invariants, this toy example can become more and more complicated. Forcing you to call the super constructor -- either explicitly or implicitly -- lets the authors of that super class establish their invariants, resulting in simpler, more maintainable code.
Every constructor calls its superclass constructor. super() call take place as the first line in the constructor. From javadoc:
If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the
no-argument constructor of the superclass. If the super class does not
have a no-argument constructor, you will get a compile-time error.
Object does have such a constructor, so if Object is the only
superclass, there is no problem.
more here
Because it says so in the Java Language Specification.
If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.
Even it has a role with Abstract classes also. we can't initialize object of abstract class. But Child class of Abstract class calls the super() method by default. So abstract class constructor can initialize its instance variables.
for example:
public abstract class TestA {
private int a;
public TestA()
{
a=10;
}
public int displayA()
{
return a;
}
abstract void display();
}
public class TestB extends TestA{
#Override
void display() {
System.out.println("this is class B");
}
}
package Abstract;
public class TestMain {
public static void main(String[] args) {
TestA obj= new TestB();
System.out.println(obj.displayA());
}
}
Output is : 10
Here you can see, when we initiating object of class TestB , by default super constructor is calling and TestA's constructor is assigning the value of a. If super will not be called by default we can't assign instance variables of abstract class.
Inheritance is basically inheriting all the properties of your parent class. So if a sub class constructor is called, it should definitely and by default inherit all its parent class properties also. In the following code, all the properties of class A should be made available in class B also, so if I just call B's constructor, all the class A's properties(except private) are also initialized and made available, meaning B has inherited A's properties
class A {
protected int a;
A() {
a=12;
System.out.println("Inside A's constructor.");
}
}
class B extends A {
B() {
System.out.println("Inside B's constructor.");
System.out.println(a);
}
}
public class ConstructorInheritance {
public static void main(String args[]) {
B b=new B();
}
}
output:
Inside A's constructor.
Inside B's constructor.
12
Imagine class C accessing an unitialized variable of class B or A. Implicitly calling constructors of class B-->class A makes sure that you are always accessing initialized variables of inherited classes(A or B)
"The Java programming language" says "A constructor in subclass can initialize its individual state, however, as keeping contract, only super class knows how to initialize super class's state".
Thus, constructor of super class have to be called. There is sequence how the constructor processed:
Call super class constructor
Initialize fields with initializers and initialization blocks
Execute body of the constructor
For more details, have a look of the book to section "3.2".
Example:
interface Outer {
default String get() {
return "hi";
}
class Inner {
String got() {
return get();
}
}
}
This yields the error
java: non-static method get() cannot be referenced from a static context.
The inner interface/class is always static; unlike with an outer class where it's non-static unless declared static.
This is how things are today and in the upcoming java 8. Is there a fundamental reason for this difference between outer classes and outer interfaces?
Update:
After reading #Radiodef´s comment I changed the inner interface to be an inner class. An outer class can't contain a non-static inner interface so the example was confusing. An inner class is really what I would like anyway.
Update: For reference. This is perfectly legal:
class Outer {
String get() {
return "hei";
}
class Inner {
String got() {
return get();
}
}
}
Maybe I misunderstood your question, but your code snippet is exactly equivalent to
interface Outer {
public default String get() {
return "hi";
}
public static class Inner {
String got() {
return get();
}
}
}
As the JLS Chapter 9.5 (Java 8) states
A member type declaration in an interface is implicitly public and
static. It is permitted to redundantly specify either or both of these
modifiers.
So if you did
Inner innerInstance = new Outer.Inner();
innerInstance.got();
what would get() be invoked on? There is no object of type Outer involved here.
Is there a fundamental reason for this difference between outer
classes and outer interfaces?
This isn't the issue. Your class code is an example of inner classes, ie. non static nested class. The interface code is an example of a static nested class. You are comparing two different things.
The equivalent example with a static nested class within an enclosing class would be
class Outer {
String get() {
return "hei";
}
public static class Inner {
String got() {
return get(); // won't compile
}
}
}
where again it doesn't make sense for get() to work since there is no corresponding (enclosing) instance to invoke it on.
If the question, as #Radiodef put it, is
why must the class be implicitly static beyond that this is the
existing spec?
then my answer is the following:
An interface, by definition, is
A point at which independent systems or diverse groups interact
An interface does not have state and it does not have behavior. It simply describes behavior. Interface members are implicitly static because an interface does not have state.
I will provide a way for you to remember it.
For a static member, it's not bound to an object of the declaring class/interface; for a non-static member, it has to bound to an object of the declaring class.
All interface's members are implicitly static, except default ones.
So, in your Exemple: static class Inner is not bound to an object of interface Outer(Since there is no meaning of object of interface), so it can't call the member of Outer.
For your legal one in the Update: class Inner is bound to an object of class Outer, so when you create an object of Inner, you also create an anonymous object of class Outer, so when you call get() in Inner::got(), the get() is called upon that anonymous object.
Hope this help.
How can I make something like this work:
class Outer {
int some_member;
abstract class InnerBase {
abstract void method();
}
}
class OuterExtendsInner extends Outer.InnerBase {
OuterExtendsInner(Outer o) { o.super(); }
void method() {
// How do I use some_member here?
// Writing Outer.this.some_member -> error about Outer not being an enclosing class
// Writing just some_member -> no-go, either
}
}
The workaround is to have a method in InnerBase that returns Outer.this and call THAT from derived classes, but is there another way?
I primarily want to extend the InnerBase from outside in order to have better code-organization, but I could move all derived classes into Outer.
The problem here is that the synthetic field which links InnerBase to Outer is a private field. Thus, we can only access the outer object from within InnerBase, or if some method or field there provides a reference to the same object.
You could do this in OuterExtendsInner:
class OuterExtendsInner extends Outer.InnerBase {
Outer o;
OuterExtendsInner(Outer o) {
o.super();
this.o = o;
}
void method() {
// now you can reference o.some_member
int x = o.some_member;
}
}
The answer is: you can't, because it would break encapsulation. Only InnerBase can have access to attributes of Outer, not OuterExtendsInner. It is not direct inheritance. InnerBase does not inherit of Outer.
I haven't tried WhiteFang34's answer. It might work, but I'm not clear on it ...
If you really want to define an extension of your inner class elsewhere than in the outer class, the most natural thing would be to define it as an extension of the inner class in another outer extending your outer class as follows:
class Outer {
int some_member;
abstract class InnerBase {
abstract void method();
}
}
class OuterExtendsOuter extends Outer {
class InnerExtendsInner extends Outer.InnerBase {
void method() {
System.out.println(some_member);
}
}
}
I haven't actually run this code either, but it should work.
Update:
Based on the comment thread, I have now compiled and run both my code above and WhiteFang34's code.
Both in fact work, but as noted in the comments by Paŭlo Ebermann, both create two copies of the outer inside the instantiated inner class.
I'm going to upvote Paŭlo's answer, and would advocate just not trying to do this by either tactic, as it's really an abuse of the inner class mechanism.
Just make your extended inner classes live inside the same outer class!
Update 2:
What happens in my code, based on runtime examination using a debugger and on examining the output from javap inspections of the classes, is that both InnerBase and OuterExtendsOuter$InnerExtendsInner have synthetic private final fields named this$0. Because no constructors are explicitly defined, the default constructors are used, and the code snippet
OuterExtendsOuter outer = new OuterExtendsOuter();
Outer.InnerBase inner = outer.new InnerExtendsInner();
causes these two fields to both reference outer.
In other words, Paŭlo's comment is entirely correct.
By further experimentation, the same actually happens if you extend InnerBase in another inner class of Outer, so it has little to do with it being defined in the same outer class or an extension of it, but is in fact an outcome of how non-static inner classes are handled generally.
I suspect this is documented somewhere, but I haven't seen that.
Probably best to mix inheritance and inner classes as little as possible!
Just have a getter method in the InnerBase?
class Outer {
int some_member;
abstract class InnerBase {
abstract void method();
protected int getSome_Member() // This is possible, because Abstract classes can have non-abstract methods.
{
return some_member;
}
}
}
class OuterExtendsInner extends Outer.InnerBase {
OuterExtendsInner(Outer o) { o.super(); }
void method() {
// you can access "some_member" now
int myNumber = getSome_Member();
}
}
Well your problem is that every instance of InnerBase (I know it's abstract) has to have a reference to an Outer object. That is part of the semantics of nested classes. Instantiating OuterExtendsInner would need such a reference.
You can avoid that making InnerBase a static nested class.
The outer class can extend the inner class iff the inner class is compiled to ".class".
Now, every time you compile the outer class it encounters the "extends innerclass" which is
not yet compiled and the compiler will throw a NoClassDefException or ClassNotFoundException.
Isn't it ? So you will never get that inner class compiled. If you can overcome this problem
then you can also extend the inner class :) .