views:

250

answers:

5

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().

+3  A: 

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.

Artefacto
+6  A: 

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 parent;
        Inner(Outer _parent) {
            parent = _parent; 
            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.

Gunslinger47
In fact, a non-static inner class is essentially syntactic sugar for the above, by my understanding.
Luke Maurer
No, a nested static class is essentially a outer level class, which is there just for packaging convenience.
Adeel Ansari
Well, right. I just mean a *non-static* inner class is syntactic sugar on top of syntactic sugar :-D
Luke Maurer
+1  A: 

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().)

Luke Maurer
+1  A: 

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.

polygenelubricants
A: 

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.

Phani