【问题标题】:Block scope variables块范围变量
【发布时间】:2013-12-10 16:16:08
【问题描述】:

这将编译

class X
{  
    public static void main(String args[])
    {
        {
            int a = 2;
        }
        {
            int a = 3;
        }       
    }
}

这不会

class X
{  
    public static void main(String args[])
    {

        int a = 2;

        {
            int a = 3;
        }       
    }
}

我希望两者都能编译(也许这是 C 的工作方式?)。是什么原因导致不能在外层块中声明一个同名的变量?

【问题讨论】:

    标签: java compiler-errors scope


    【解决方案1】:

    简短的回答是:因为这是 JLS §6.4 中定义 Java 语言的方式。

    您可能会使用其他语言,所谓的variable shadowing 是允许的。然而,Java 语言的发明者认为这是他们不希望在他们的语言中出现的尴尬特性:

    此限制有助于检测一些原本非常隐蔽的错误。

    但是,正如作者在 JLS 的同一部分中所述,您会在 Java 的其他地方发现阴影:

    对局部变量对成员的阴影的类似限制是 判断为不切实际,因为在超类中添加成员 可能导致子类必须重命名局部变量。有关的 考虑通过以下方式限制局部变量的阴影 嵌套类的成员,或局部变量的局部阴影 在嵌套类中声明的变量也没有吸引力。

    这意味着在实践中以下代码是合法的:

    class A {
       int x = 0;
       void m() {
         int x = 10; // Shadows this.x
       }
    }
    

    正如作者所描述的,允许通过声明具有相同名称的方法局部变量来隐藏实例变量,因为有人可能会在您无法再编译类的某一天扩展 A 的功能B 如果遮蔽是非法的:

    class B extends A {
       void m() {
         int x = 10; // Shadows A.this.x if A declares x
       }
    }
    

    如果您考虑像 C 这样允许阴影的语言,您会发现如下笨拙的代码:

    int x;
    int main() 
    {
      {
        int x = 0;
        {
          extern int x;
          x = 1;
        }
        printf("%d\n", x); // prints 0
      }
      printf("%d\n", x); // prints 1
      return 0;
    }
    

    这个程序不太容易理解,因此可能不会产生您期望的结果,这要归功于可变阴影。

    【讨论】:

    • 关于上面class A定义中的int x = 10; // Shadows A.this.x if A declares x这是否意味着当我在A中打印x时,直到打印100,因为它打印0
    • 别在意上面的 ^^^ 知道shadowing 是关于什么的。如果我没记错的话,它的意思是overriding 同名的外部范围变量
    【解决方案2】:

    Java 不允许您在彼此的范围内拥有两个同名的变量。

    在你的第二种情况下:

    int a = 2;
    
    {
      // the outer 'a' is still in scope
      int a = 3; // so this is a redeclare <-- nooo!
    } 
    

    但是,在您的第一种情况下,每个 a 都包含在自己的范围内,所以一切都很好。

    【讨论】:

    • 小心Java doesn't allow you to have two variables with the same name within scope of one another.的声明这仅适用于local variables, method, constructor, and exception parameters
    • @SotiriosDelimanolis 您引用的链接如何支持您的陈述?
    • @flow2k 它没有。它只是定义局部变量和参数。范围定义为here
    【解决方案3】:

    因为在第二种情况下,a 在静态块中是已知的,所以您尝试重新声明它。编译器不允许你这样做:

    public static void main(String args[]) {
        {
           int a = 2; //a is known only here
        }             //a will be freed
        {
           int a = 3; //you can declare it again here
        }       
    }
    

    【讨论】:

      【解决方案4】:
      public static void main(String args[])
      {
          int a = 2; // I know a
          // I know a
          {
              // I know a
              int a = 3; // There can be only one a!
          }       
      }
      

      在上面的示例中,您在方法 main() 中声明了 a。从声明到方法结束,声明了a。在这种情况下,您不能在代码块中重新声明 a。

      下面,您在一个块中声明a。它只在内部知道。

      public static void main(String args[])
      {
          { 
              int a = 2; // I know a
              // I know a
          }
          // Who is a?
          {
      
              int a = 3; // I know a!
          }       
      }
      

      【讨论】:

        【解决方案5】:

        在 Java 中,所有局部变量都将存储在 Stack 中。所以如果你写

        class X
        {  
        public static void main(String args[])
        {
           int a = 2;    // At this point var 'a' is stored on Stack
           {
               /*
               Now as the prev. 'main method is not yet complete so var 'a' is still present on the Stack. So at this point compiler will give error "a is already defined in main(java.lang.String[])" 
                */
               int a = 3;   
        
           }
         }
        }
        

        希望对你有所帮助

        谢谢

        【讨论】:

        • 这是完全错误的。编译后的代码不知道局部变量的名称,它只知道索引。此外,变量不一定同时位于堆栈上。每个方法框架都有一个操作数堆栈和一个局部变量数组。 在 Java 中,所有局部变量都将存储在 Stack 中因此是不准确的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-09-10
        • 2022-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-10
        相关资源
        最近更新 更多