块作用域变量

2022-09-03 05:06:35

这将编译

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的工作方式?为什么原因,因为不可能在块中声明与外部块中具有相同名称的变量?


答案 1

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

您可能从其他语言中使用,允许这种所谓的变量阴影。然而,Java语言的发明者认为这是一个他们不想要的语言的尴尬功能:

此限制有助于检测一些其他非常晦涩的错误。

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

对局部变量对成员的阴影的类似限制被认为是不切实际的,因为在超类中添加成员可能导致子类必须重命名局部变量。相关注意事项使得嵌套类成员对局部变量的阴影的限制,或者对嵌套类中声明的局部变量对局部变量的阴影的限制也没有吸引力。

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

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

正如作者所描述的那样,允许通过声明具有相同名称的方法局部变量来影射实例变量,因为如果有人有可能在某一天扩展的功能,如果阴影是非法的,您将无法再编译类:AB

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;
}

由于可变阴影,该程序并不容易遵循,因此可能无法产生您期望的结果。


答案 2

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

在第二种情况下:

int a = 2;

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

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