Why do two programs have forward referencing errors while the third does not?

3.3k views Asked by At

The following does not compile, giving an 'illegal forward reference' message:

class StaticInitialisation {

    static
    {
        System.out.println("Test string is: " + testString);
    }

    private static String testString;

    public static void main(String args[]) {
        new StaticInitialisation();
    }
}

However, the following does compile:

class InstanceInitialisation1 {

    {
        System.out.println("Test string is: " + this.testString);
    }

    private String testString;

    public static void main(String args[]) {
        new InstanceInitialisation1();
    }
}

But the following does not compile, giving an 'illegal forward reference' message:

class InstanceInitialisation2 {

        private String testString1;

    {
        testString1 = testString2;
    }

    private String testString2;

    public static void main(String args[]) {
        new InstanceInitialisation2();
    }
}

Why do StaticInitialisation and InstanceInitialisation2 not compile, while InstanceInitialisation1 does?

4

There are 4 answers

2
Jon Skeet On BEST ANSWER

This is covered by section 8.3.3 of the JLS:

Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope (ยง6.3). Specifically, it is a compile-time error if all of the following are true:

  • The declaration of a class variable in a class or interface C appears textually after a use of the class variable;

  • The use is a simple name in either a class variable initializer of C or a static initializer of C;

  • The use is not on the left hand side of an assignment;

  • C is the innermost class or interface enclosing the use.

Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. Specifically, it is a compile-time error if all of the following are true:

  • The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;

  • The use is a simple name in either an instance variable initializer of C or an instance initializer of C;

  • The use is not on the left hand side of an assignment;

  • C is the innermost class or interface enclosing the use.

In your second case, the use isn't a simple name - you've got this explicitly. That means it doesn't comply with the second bullet in the second list quoted above, so there's no error.

If you change it to:

System.out.println("Test string is: " + testString);

... then it won't compile.

Or in the opposite direction, you can change the code in the static initializer block to:

System.out.println("Test string is: " + StaticInitialisation.testString);

Odd, but that's the way it goes.

0
Saif On

Lets look at these two example, I guess this will make you clear.

public class InstanceAndSataticInit {

    {
        System.out.println("Test string is (instance init): " + this.testString);
    }

    static{
        System.out.println("Test string is (static init ): " + InstanceAndSataticInit.testStringStatic);
    }

    public  static String testStringStatic="test";
    public  String testString="test";

    public static void main(String args[]) {
        new InstanceAndSataticInit();
    }

}

Output:

Test string is (static init ): null
Test string is (instance init): null

And

public class InstanceAndSataticInitVariableFirst {

    public  static String testStringStatic="test";
    public  String testString="test";

    {
        System.out.println("Test string is (instance init): " + this.testString);
    }

    static{
        System.out.println("Test string is (static init ): " + InstanceAndSataticInitVariableFirst.testStringStatic);
    }



    public static void main(String args[]) {
        new InstanceAndSataticInitVariableFirst();
    }


}

output :

Test string is (static init ): test
Test string is (instance init): test

So you can say the sequence are like this.

  1. Static variable will be created but not be initialized.

  2. Static initialization will be executed according to the given sequence.

  3. Non Static variable will be created but not be initialized.
  4. Non Static initialization will be executed according to the given sequence.

By sequence I mean appearance in the code.

I guess this steps answer your two not working example StaticInitialisation and InstanceInitialisation2

But in case your second working example InstanceInitialisation1 by using this key word you are actually helping compiler to overlook the textual hierarchy. Same thing happen in case of static when I call InstanceAndSataticInit.testStringStatic in my first example InstanceAndSataticInit

0
Pulipati Prasadarao On

Here what we have to understand is that in 2nd code snippet You are using block and this keyword.

  1. The block executes if object is created.
  2. That means the object is created in heap area.
  3. You are externally using this keyword to get a value of instance variable.
  4. Here object created with default values that will return as value.
  5. With out using this keyword you can't compile 2nd snippet also.
0
ZhongYu On

Simple reason - it's too expensive or impossible to analyze and forbid all forward references. e.g.

{
    print( getX();  );    // this.x
    print( that().x );    // this.x
}

int x;
int getX(){ return x; }

This that(){ return this; }

The spec settles on forbidding some simple cases indicative of common programmer mistakes.

See also Recursive initializer works when I add "this"?