10

以下方法不起作用,因为内部块声明了一个与外部块中的变量同名的变量。显然变量属于声明它们的方法或类,而不是声明它们的块,因此我不能编写一个简短的小临时块进行调试,碰巧将外部范围内的变量推入影子只是片刻:

void methodName() {
  int i = 7;
  for (int j = 0; j < 10; j++) {
    int i = j * 2;
  }
}

几乎我用过的所有块级语言都支持这一点,包括我在学校编写的解释器和编译器的琐碎小语言。Perl 可以做到这一点,Scheme 也可以,甚至 C 也可以。甚至 PL/SQL 也支持这一点!

这个 Java 设计决策的基本原理是什么?

编辑:正如有人指出的那样,Java 确实有块作用域。我要问的概念的名称是什么?我希望我能从那些语言设计课程中记住更多。:)

4

6 回答 6

26

好吧,严格来说,Java确实有块范围的变量声明。所以这是一个错误:

void methodName() {
  for (int j = 0; j < 10; j++) {
    int i = j * 2;
  }
  System.out.println(i); // error
}

因为 'i' 在 for 块之外不存在。

问题是 Java 不允许您创建与在同一方法的外部块中声明的另一个变量同名的变量。正如其他人所说,据说这样做是为了防止难以识别的错误。

于 2008-09-26T19:00:49.957 回答
19

因为作家故意这样做并通过忘记现在有两个具有相同名称的变量而完全搞砸的情况并不少见。他们更改了内部变量名称,但保留了使用该变量的代码,现在无意中使用了先前隐藏的变量。这导致程序仍然可以编译,但执行错误。

同样,意外隐藏变量并改变程序行为的情况并不少见。在不知不觉中隐藏现有变量可以像我上面提到的取消隐藏变量一样容易地更改程序。

允许这种阴影几乎没有什么好处,以至于他们将其排除在外,因为它太危险了。说真的,只需将您的新变量称为其他变量,问题就会消失。

于 2008-09-26T18:37:34.613 回答
14

我相信基本原理是大多数时候,这不是故意的,这是一个编程或逻辑缺陷。

在像您这样微不足道的示例中,这很明显,但是在一大段代码中,意外地重新声明变量可能并不明显。

ETA:它也可能与java中的异常处理有关。我认为这个问题的一部分是在一个与为什么在 try 部分中声明的变量在 catch/finally 范围中不可用有关的问题中讨论的。

于 2008-09-26T18:33:14.953 回答
10

我猜它会导致难以发现的错误。在 C# 中类似。

Pascal 不支持这一点,因为您必须在函数体上方声明变量。

于 2008-09-26T18:30:51.713 回答
1

这个问题的基本假设是错误的。

Java确实有块级作用域。但它也有一个范围层次结构,这就是为什么你可以ifor循环内引用,但不能j在 for 循环外引用的原因。

public void methodName() {
  int i = 7;
  for (int j = 0; j < 10; j++) {
    i = j * 2;
  }

  //this would cause a compilation error!
  j++;
}

我一生都无法弄清楚为什么您希望范围界定以其他方式表现。不可能确定i您在 for 循环中指的是哪个,我敢打赌,您有 99.999% 的机会想要引用i方法内部。

于 2008-09-26T19:27:25.010 回答
0

另一个原因:如果允许这种变量声明,人们会想要(需要?)一种访问外部块变量的方法。可能会添加类似“外部”关键字的内容:

void methodName() {
    int i = 7;
    for (int j = 0; j < 10; j++) {
        int i = outer.i * 2;
        if(i > 10) {
            int i = outer.outer.i * 2 + outer.i;
        }
    }
}
于 2016-05-13T06:30:38.750 回答