代码之家  ›  专栏  ›  技术社区  ›  Mike

为什么Java有一个“不可访问语句”编译器错误?

  •  78
  • Mike  · 技术社区  · 14 年前

    class Test {
            public static void main(String args[]) {
                    System.out.println("hello world");
                    return;
                    System.out.println("i think this line might cause a problem");
            }
    }
    

    当然,这会产生编译器错误。

    测试.java:7:无法访问的语句

    这仅仅是Java想当保姆,还是有充分的理由让它成为编译器错误?

    8 回复  |  直到 7 年前
        1
  •  64
  •   Community CDub    8 年前

    因为无法访问的代码对编译器来说毫无意义。尽管让代码对人有意义比让它对编译器有意义更重要、更困难,但编译器是代码的主要消费者。Java的设计者认为对编译器没有意义的代码就是一个错误。他们的立场是,如果您有一些无法访问的代码,您就犯了一个需要修复的错误。

    Unreachable code: error or warning? ,其中作者说“我个人强烈地感觉到这应该是一个错误:如果程序员写了一段代码,它应该总是有在某些场景中实际运行它的意图。”显然,Java的语言设计者同意这一点。


    许多人在评论中指出,有许多类的代码是不可访问的,Java并不阻止编译。如果我正确理解Gdel的结果,那么没有编译器能够捕获所有不可访问代码的类。

    单元测试不能捕获每一个bug。我们不会用这个来反驳他们的价值。同样地,编译器不能捕获所有有问题的代码,但是在可能的情况下防止编译坏代码对它来说仍然是有价值的。


    (在您否决之前:问题不是Java是否应该有一个不可访问的语句编译器错误。问题是 为什么? Java有一个无法访问的语句编译器错误。不要因为你认为Java做了错误的设计决定就对我投反对票。)

        2
  •  47
  •   irreputable    9 年前

    if (true) return;
    

    这看起来很荒谬,任何读过代码的人都会猜到这一定是故意的,而不是一个让其他语句无法访问的粗心错误。

    Java对“条件编译”有一点支持

    http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

    if (false) { x=3; }
    

    不会导致编译时 错误。优化编译器可能 认识到x=3;语句将 技术意义上的“遥不可及”

    这种情况的基本原理是不同的 治疗就是让程序员 定义“标志变量”,例如:

    static final boolean DEBUG = false;
    

    然后编写如下代码:

    if (DEBUG) { x=3; }
    

    想法是这应该是可能的 将DEBUG的值从 从真到真或从真到假 然后正确编译代码 文本。

        3
  •  19
  •   Brady Moritz    14 年前

    是保姆。 我觉得.Net做对了这一点-它对无法访问的代码发出警告,但不是错误。有人警告这是好事,但我认为没有理由阻止编译(特别是在调试会话期间,在调试会话中,最好抛出一个返回值来绕过一些代码)。

        4
  •  14
  •   Pawel Veselov Samarpan Patel    11 年前

    我只是注意到这个问题,想把我的0.02美元加到上面。

    对于Java,这实际上不是一个选项。“无法访问的代码”错误并不是因为JVM开发人员认为要保护开发人员免受任何伤害,或者格外警惕,而是因为JVM规范的要求。

    Java编译器和JVM都使用所谓的“堆栈映射”(stack maps)——关于堆栈上所有项的明确信息,分配给当前方法。堆栈中每个插槽的类型都必须是已知的,这样JVM指令就不会将一种类型的项误认为另一种类型的项。这对于防止将数值用作指针非常重要。使用Java汇编,可以尝试推送/存储一个数字,但随后弹出/加载一个对象引用。然而,JVM在类验证期间会拒绝这些代码,也就是在创建和测试堆栈映射的一致性时。

    为了验证堆栈映射,VM必须遍历一个方法中存在的所有代码路径,并确保无论执行哪个代码路径,每个指令的堆栈数据都与堆栈中任何先前的代码推送/存储的一致。简单来说:

    Object a;
    if (something) { a = new Object(); } else { a = new String(); }
    System.out.println(a);
    

    在第3行,JVM将检查“if”的两个分支是否只将与对象兼容的内容存储到一个(这只是本地var#0)中(因为第3行及以后的代码就是这样处理本地var#0的)。

    当编译器到达一个不可访问的代码时,它不知道堆栈在那个时候可能处于什么状态,所以它不能验证它的状态。此时它不能再编译代码了,因为它也不能跟踪局部变量,所以它没有在类文件中留下这种模糊性,而是产生了一个致命的错误。

    if (1<2) 将愚弄它,但这并不是真正的愚弄-它给它一个潜在的分支,可以导致代码,至少编译器和虚拟机都可以决定,如何从那里开始使用堆栈项。

    另外,我不知道.NET在这种情况下会做什么,但我相信它也会失败。对于任何机器代码编译器(C、C++、Obj-C等),这通常不会是个问题。

        5
  •  5
  •   Ricky Clarkson    14 年前

    编译器的目标之一是排除错误类。有些不可访问的代码是偶然出现的,javac在编译时排除了这类错误是很好的。

    对于每个捕获错误代码的规则,有人会希望编译器接受它,因为他们知道自己在做什么。这是编译器检查的代价,而获得正确的平衡是语言设计的关键之一。即使经过最严格的检查,仍然有无限多的程序可以编写,所以事情不会那么糟。

        6
  •  5
  •   Sean Patrick Floyd    14 年前

    虽然我认为这个编译器错误是一件好事,但有一种方法可以解决它。 使用一个你知道是真的条件:

    public void myMethod(){
    
        someCodeHere();
    
        if(1 < 2) return; // compiler isn't smart enough to complain about this
    
        moreCodeHere();
    
    }
    

    编译器不够聪明,不能抱怨这一点。

        7
  •  0
  •   Jérôme JEAN-CHARLES    14 年前

    抱怨编译器越严格越好当然是件好事,因为它允许您做您需要的事情。

        8
  •  0
  •   MichaelS    13 年前

    如果允许的原因 if (aBooleanVariable) return; someMoreCode; 是允许标志,那么事实是 if (true) return; someMoreCode; 不生成编译时错误似乎与生成CodeNotReachable异常的策略不一致,因为编译器“知道” true 不是标志(不是变量)。

    另外两种方法可能很有趣,但不适用于关闭方法代码的一部分 if (true) return :

    现在,与其说 if (true) return; 你可能想说 assert false 并添加 -ea OR -ea package OR -ea className

    还有一个问题 System.exit(0) 是的,但这可能是一种过度杀伤力,如果您将它放在JSP中的Java中,那么它将终止服务器。

    除了java是一种“保姆”语言,我更喜欢使用类似C/C++的本地语言来进行更多的控制。