【问题标题】:Java "new String[-1]" passes compilation. How come?Java "new String[-1]" 通过编译。怎么会?
【发布时间】:2012-05-17 13:11:05
【问题描述】:

在 Java 中摆弄时,我初始化了一个负长度的新字符串数组。 即 -

String[] arr = new String[-1];

令我惊讶的是,编译器并没有抱怨它。 谷歌搜索没有提出任何相关的答案。谁能解释一下这个问题?

非常感谢!

【问题讨论】:

  • 编译器不检查数组的大小,但是如果大小为负则抛出java.lang.NegativeArraySizeException
  • String[] arr = new String[someNumberThatMightOrMightNotBeNegative]; - 在编译时检查那个,我敢!
  • @Izkata - 我做到了,使用 Eclipse,但它仍然非常满意(即 - 没有警告或任何东西)
  • @TomTeman 你误会了。编译器必须实际运行代码来确定数字是否为负数,即使这样,它也必须尝试所有可能的代码路径,包括所有可能的输入,以确定这个数字是否为负。这简直是​​不可能的。因此语言设计者决定在运行时进行检查,因为这会同时捕获您在问题中的版本和我上面评论中的版本。
  • 大声笑..简单的答案..切换到 C#

标签: java arrays compilation


【解决方案1】:

原因是 JLS 允许这样做,而将其标记为编译错误的编译器将拒绝有效的 Java 代码。

JLS 15.10.1 中指定。这是相关的sn-p:

"...如果任何 DimExpr 表达式的值小于零,则抛出 NegativeArraySizeException。"

现在,如果 Java 编译器将代码标记为错误,那么指定的行为就不会发生......在该特定代码中。

此外,在涉及编译时常量表达式(如-1)的“明显错误”情况下,我找不到任何文本“授权”编译器拒绝它。 (谁能说这真的是一个错误?)


当然,下一个问题是“为什么 JLS 允许这样做?”

您需要询问 Java 设计人员。但是我可以想到一些(大部分)合理的原因:

  • 这最初被忽略了,并且没有强有力的理由来修复它。 (注意修复它会破坏源代码的兼容性。)

  • 它被认为太不寻常/不值得处理。

  • 这可能会给编写源代码生成器的人带来问题。 (想象一下,为了不生成不可编译的代码,必须编写代码来评估编译时常量表达式。使用当前的 JLS 规范,您可以简单地生成“坏”大小的代码,并处理如果代码被执行,则异常(或不)。)

  • 也许有人计划在 Java 中添加“非数组”:-)


其他答案表明编译器可以/应该“标记”这种情况。如果“标记”意味着输出警告消息,那肯定是 JLS允许。但是,编译器是否应该 这样做是有争议的。一方面,如果上面的代码是错误编写的,那么标记该错误会很有用。另一方面,如果它不是错误(或“错误”不相关),那么警告将是噪音,或者更糟。无论哪种方式,这都是您需要与相应编译器的维护者讨论的问题。

【讨论】:

  • 下一个问题当然是“为什么 JLS 允许这样做?”
  • 不能仅仅因为DimExpr 不允许使用负的编译时间常数而不能发生指定的行为是不正确的。
  • @MarkoTopolnik - 您的评论已被记录,但我不同意。
  • 您是否声称当前发生“指定行为”的唯一方法是在执行new String[<negative_compile_time_constant>] 时?因为像int i = -1; new String[i]; 这样的代码已经超出了编译时常量的范围。
  • 不,我没有。我声称如果new String[-1] 被标记为编译错误,那么new String[-1] 不能抛出异常。 JLS 声明它应该抛出一个异常......没有关于编译时常量表达式的任何限定。这是我对 JLS 的解释。无论如何,这是一个愚蠢的狡辩,因为事实证明这不是编译错误,并且 JLS 中没有任何内容表明它应该是编译错误。
【解决方案2】:

我看不出为什么不能在编译时标记出来(至少作为警告),因为这是unconditionally throws NegativeArraySizeException when executed

我用我的编译器做了一些快速的实验,对这种事情似乎出奇的放松。它不会对常量表达式中的整数除以零、使用常量索引的越界数组访问等发出警告。

由此我得出结论,这里的一般模式是信任程序员。

【讨论】:

  • 另外,检查 Pau 的第一个块代码,为什么不在编译时或运行时限制数组的“-1”大小是有道理的(这里我的意思是传递 -1 值,它然后会引发异常)
【解决方案3】:

编译器只负责检查语言的语法,而不是你的代码的语义。 因此,编译器没有抱怨错误是合理的,因为您的代码中根本没有语法错误。

在Java中,数组是在runtime分配的,这绝对没问题。如果是在编译时分配,那么编译器如何检查下面的代码?

// runtime pass the length, with any value
void t(int length) {
   String[] stirngs = new String[length];
}

将负值作为长度传递给构造数组时,会抛出运行时异常。

public class Main {

    public static void main(String[] args) {
        String[] v = new String[-1];
    }
}

有错误:

Exception in thread "main" java.lang.NegativeArraySizeException
    at Main.main(Main.java:5)

【讨论】:

  • 那么死代码检测呢?那是语法还是语义?死代码是编译器错误。 Java 中还有许多其他此类功能,例如检查每个代码路径是否导致返回语句。
  • 编译器肯定会检测到没有返回的方法,但不会检测到一些语义死代码,如` if(true) { System.out.println("true"); } else { System.out.println("死代码"); }`。只有 IDE 会标记为警告。
  • 但是有两个返回和第三个丢失的方法——这也是语义死代码。否则它将被 CFG 消除,而不是通过复杂的死代码检测算法。静态分析不是语法检查。也不是类型检查,也不是检查方法调用的有效性等。Java 编译器会根据 JLS 进行大量语义检查。
  • @PauKiatWee :这是错误的“编译器只负责检查语言语法,而不是你代码的语义。”。在此处检查编译器阶段mec.ac.in/resources/notes/notes/compiler/Module1/intro.html
【解决方案4】:

Java 编译器将整数作为数组的长度。它可以是变量或编译时常量。数组的长度是在创建数组时确定的。创建后,它的长度是固定的。

编译器应该将一个负的编译时常量标记为数组的长度。它只是不这样做。如果长度为负数,您将在运行时收到 NegativeArraySizeException。

【讨论】:

  • “编译器应将负编译时常量标记为数组的长度”根据@Stephen C 的回答是不准确的。这样做会违反 JLS。
  • @LawrenceJohnston :我的意思是“编译器不会将负编译时常量标记为数组的长度。它应该标记它,因为编译器很容易实现检查”
猜你喜欢
  • 2011-09-06
  • 2021-11-26
  • 2014-01-09
  • 1970-01-01
  • 2020-11-13
  • 2012-03-27
  • 2016-07-25
  • 2018-05-19
  • 1970-01-01
相关资源
最近更新 更多