阻止范围变量

Pao*_*olo 13 java scope compiler-errors

这将编译

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

这不会

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

        int a = 2;

        {
            int a = 3;
        }       
    }
}
Run Code Online (Sandbox Code Playgroud)

我期望两者都编译(也许这是C的工作方式?).原因是什么,因为无法在外部块中使用相同名称的块声明变量?

Raf*_*ter 15

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

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

此限制有助于检测一些非常模糊的错误.

但是,您可以在Java的其他位置找到阴影,因为作者在JLS的同一部分中声明:

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

这实际上意味着以下代码是合法的:

class A {
   int x = 0;
   void m() {
     int x = 10; // Shadows this.x
   }
}
Run Code Online (Sandbox Code Playgroud)

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

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

如果你考虑像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;
}
Run Code Online (Sandbox Code Playgroud)

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


Dun*_*nes 6

Java不允许您在两个相同的范围内拥有两个相同名称的变量.

在你的第二种情况:

int a = 2;

{
  // the outer 'a' is still in scope
  int a = 3; // so this is a redeclare <-- nooo!
} 
Run Code Online (Sandbox Code Playgroud)

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