Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hierarchy of inner classes in Java

I am using a hierarchy of inner classes to represent some data in an application and I have run into an error message that I simply do not understand. My code can be boiled down to the following minimal example:

public class A {
    public class B extends A {}
    public class C extends B {}
}

Javac (and my IDE of course) fails to compile the code with the following error message:

A.java:3: cannot reference this before supertype constructor has been called
    public class C extends B {}
           ^
1 error

I didn't write this anywhere. There is no more code than provided above, so I assume javac has generated something related to the inner class.

I have found another way to represent my data, so I am simply interested in a good explanation of why it doesn't compile.

like image 514
Mathias Schwarz Avatar asked Jul 12 '11 07:07

Mathias Schwarz


3 Answers

You need an outer class instance to create an inner class instance i.e something like new Outer().new Inner();

To extend the inner class (Parent inner class) with another inner class (child inner class), you cannot call the constructor of 'parent inner class' because the instance of 'outer class' is not there.

Try like this,

public class A{        
    public class B extends A {
        B() { }  
    }    

    public class C extends B {
        C() { 
            new A().super();  
        }  
    }    

    public static void main(String args[])  {

    }      
}

Similar question : Odd situation for “cannot reference this before supertype constructor has been called”

like image 171
Saurabh Gokhale Avatar answered Oct 23 '22 03:10

Saurabh Gokhale


The other poster is correct, but how to fix? Simply make your class static:

public class A {
    public static class B extends A {}
    public static class C extends B {}
}

Note that if your inner classes refer to fields of the outer class, you can't make them static, otherwise you can (and should - doing so reduces dependencies).

like image 2
Bohemian Avatar answered Oct 23 '22 04:10

Bohemian


Your code compiles under Java 7.

The following workaround compiles under Java 6.

    public class C extends B
    {
        public C()
        {
            A.this.super();
        }
    }

@saugok's link to the previous question quoted Joshua's explanation. Basically he argued that since C is subclass of A, C inherits A's members as C's members. Therefore B is also C's member. (For example a class literal C.B.class is valid.) Therefore he argues that C.this is the enclosing instance for B's super(), therefore C(){super();} is actually C(){C.this.super();}. Since C.this cannot be evaluated before super constructor, thus the error.

However this doesn't seem to be warranted by the language spec. See #8.1.3. Since B is not immediately lexically enclosed by C, B is not a direct inner class of C, there is no reason to say that B's direct enclosing instance must be an instance of C.

We need to pass B() an instance of A. It is true that C.this is an instance of A ( try this code: new C().new B().new C().new B();) therefore it could be a candidate. There is also another candidate, A.this. A.this is available and ready to use (it's passed in as the hidden parameter to C()).

According to javap, javac 7 compiles the code into

class B
    private A this$0;
    B( A a )
        this$0 = a;
        super(); // A()

class C extends B
    private A this$0;
    C( A a )
        this$0 = a;
        super( a ); // B(A)
like image 1
irreputable Avatar answered Oct 23 '22 05:10

irreputable