错误的前向参考的有趣问题

时间:2014-08-29 14:02:16

标签: java compiler-errors

来自JLS的众所周知的example关于incorrect forward reference错误:

class Test1 {
   int i = j; // compile-time error:
   // incorrect forward reference
   int j = 1;
}

好的,我说,并使用关键字this

应用众所周知的“hack”
class Test1 {
    int i = ++this.j;
    {
// line#4: System.out.println("j = " + j); // compile-time error: illegal forward reference
        System.out.println("j = " + this.j);
    }
    int j = this.j + 1;
    {
        System.out.println("j = " + j);
    }
}

输出将是:

j = 1
j = 2

谁可以解释为什么我无法访问第4行中的变量j

是否在此行中初始化变量j

如果不是,我如何在下一步1this.j获得价值2

如果是,为什么我无法通过simple name访问?

2 个答案:

答案 0 :(得分:1)

看起来正在发生的事情类似于this question中发生的事情。 如果您设法解决语言限制并引用未初始化的变量,它将具有默认值(在int的情况下为0)。

所以在这种情况下,你打电话给int i = ++this.j。由于尚未初始化,因此它取值0,然后递增。在您的下一个电话中,它会保留之前的值,并为其添加一个值。

我发现another answer更详细地介绍了初始化的限制。关键部分是这个

  

成员的声明需要以文本形式出现   仅当成员是实例(分别是静态)字段时才使用   一个类或接口C和所有以下条件保持

     

...

     
      
  • 用法是通过简单的名称
  •   

所以基本上,你所做的只是因为语言设计师并没有明确禁止它。

要处理问题的其他部分,我会引用Java documentation

  

Java编译器将初始化程序块复制到每个构造函数中。

所以你编写的初始化代码发生在对象的构造函数中。创建对象时,将使用值0初始化两个变量,然后运行代码以修改这些值。所以不行,int i = ++this.j;行没有初始化j。这会在到达该行之前自动发生。

答案 1 :(得分:0)

问:是否在此行中初始化变量j?**

是的,它已初始化,如下面的java代码类文件中所示。

// Compiled from Test1.java (version 1.6 : 50.0, super bit)
public class com.test.java.Test1 {

  // Field descriptor #6 I
  int i;

  // Field descriptor #6 I
  int j;

  // Method descriptor #9 ()V
  // Stack: 4, Locals: 1
  public Test1();
     0  aload_0 [this]
     1  invokespecial java.lang.Object() [11]
     4  aload_0 [this]
     5  aload_0 [this]
     6  dup
     7  getfield com.test.java.Test1.j : int [13]
    10  iconst_1
    11  iadd
    12  dup_x1
    13  putfield com.test.java.Test1.j : int [13]
    16  putfield com.test.java.Test1.i : int [15]
    19  aload_0 [this]
    20  aload_0 [this]
    21  getfield com.test.java.Test1.j : int [13]
    24  iconst_1
    25  iadd
    26  putfield com.test.java.Test1.j : int [13]
    29  return
      Line numbers:
        [pc: 0, line: 4]
        [pc: 4, line: 5]
        [pc: 19, line: 10]
        [pc: 29, line: 4]
      Local variable table:
        [pc: 0, pc: 30] local: this index: 0 type: com.test.java.Test1
}

关于为什么你不能用简单名称访问j的问题,它可能与编译器如何智能地重新组织并用this.j替代对j的直接访问有关。这看起来当你尝试在初始化块中访问它时不会发生(我不知道的原因),但下面的代码使用直接引用(可能是因为编译器在我们尝试访问j时巧妙地放置了这个引用)

public Test1() {
        System.out.println(j);
    }
    int i = ++this.j;
    {       
        // line#4: System.out.println("j = " + j); // compile-time error: illegal forward reference
        //System.out.println("j = " + this.j);
    }
    int j = this.j + 1;