7

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.

Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
Mathias Schwarz
  • 7,099
  • 23
  • 28

3 Answers3

8

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”

Community
  • 1
  • 1
Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
  • +1 for interesting workaround. I haven't seen that done before :) (I still like my answer better) – Bohemian Jul 12 '11 at 08:07
  • @Bohemian : LOL ! Everyone likes his **own** answers :P. Anyway, your answer also sounds good. – Saurabh Gokhale Jul 12 '11 at 08:13
  • Ok, thanks for the explanation. Your work-around will of course make the code compile, but it will give me two different instances of A both pointed to by C. If I actually exploit the fact that C is not static, that will open a whole new world of gotcha's :-) – Mathias Schwarz Jul 12 '11 at 08:14
2

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

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • +1. Important to understand what static/non-static means for inner classes. – Henning Jul 12 '11 at 08:09
  • But doing that will kill the pointer to the outer class, and I actually needed them to me non-static. I ended up doing something like that and managing a reference to the outer class myself. – Mathias Schwarz Jul 12 '11 at 08:10
1

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)
irreputable
  • 44,725
  • 9
  • 65
  • 93