Eclipse的一个bug?只有默认情况下开启了一个null。

31

我正在尝试使用enum,我发现下面的代码可以在Eclipse上编译并运行(Build id: 20090920-1017,不确定确切的编译器版本):

public class SwitchingOnAnull {
    enum X { ,; }
    public static void main(String[] args) {
        X x = null;
        switch(x) {
            default: System.out.println("Hello world!");
        }
    }
}

在使用Eclipse编译和运行时,这将打印"Hello world!"并正常退出。

但是,在使用javac编译器时,会按预期抛出NullPointerException

那么,Eclipse Java编译器中是否存在 bug?


你在eclipse.org上报过bug吗? - Goibniu
或者在 Eclipse 论坛上发布。 - fastcodejava
4
@Rulmeq,@fastcodejava:请查看我的回答。已经确认了错误,并指派此问题为3.6.1版本的候选问题。 - polygenelubricants
@poly - 没有任何冒犯之意。很高兴你也在这里发帖。 - fastcodejava
请参见http://www.riedquat.de/blog/2011-03-04-02。 - Raedwald
3个回答

27
这是一个bug。以下是《Java语言规范第3版》中switch语句的指定行为:

JLS 14.11 switch语句

SwitchStatement:
    switch ( Expression ) SwitchBlock

When the switch statement is executed, first the Expression is evaluated. If the Expression evaluates to null, a NullPointerException is thrown and the entire switch statement completes abruptly for that reason.

显然,Eclipse上的错误与default case或enum没有任何关系。

public class SwitchingOnAnull {
    public static void main(String[] args) {        
        java.math.RoundingMode x = null;
        switch(x) {};

        switch((Integer) null) {};

        switch((Character) null) {
            default: System.out.println("I've got sunshine!");
        }       
    }
}

上面的代码在Eclipse中编译和运行“良好”。每个单独的switch在使用javac编译时都会抛出NullPointerException,这正是规范所要求的。

原因

以下是在Eclipse下编译的javap -c SwitchingOnAnull
Compiled from "SwitchingOnAnull.java"
public class SwitchingOnAnull extends java.lang.Object{
public SwitchingOnAnull();
Code:
 0: aload_0
 1: invokespecial  #8; //Method java/lang/Object."<init>":()V
 4: return

public static void main(java.lang.String[]);
Code:
 0: aconst_null
 1: astore_1
 2: getstatic     #16; //Field java/lang/System.out:Ljava/io/PrintStream;
 5: ldc           #22; //String I've got sunshine!
 7: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return

}

看起来Eclipse编译器完全摆脱了switch结构。不幸的是,这种优化破坏了语言规范。


官方声明

该错误已被提交并分配进行修复。

Olivier Thomann 2010-05-28 08:37:21 EDT

We are too aggressive on the optimization.

For:

  switch((Integer) null) {};

we optimize out the whole switch statement when we should at least evaluate the expression.

I'll take a look.

Candidate for 3.6.1.

参见


奇怪。Eclipse怎么会引起这个bug呢?Eclipse不是在后台使用Java和Javac吗?非常有趣。 - D.C.
5
有趣。某个EDT开发人员在实现优化时似乎有点“过于”聪明。 - aioobe
1
似乎没有被注册为错误:https://bugs.eclipse.org/bugs/buglist.cgi?type1-0-0=substring;field1-0-3=status_whiteboard;short_desc=compiler%20switch%20enum%20null;field0-0-0=product;field1-0-2=short_desc;type0-0-1=substring;field0-0-1=component;type1-0-1=substring;type1-0-2=substring;type1-0-3=substring;type0-0-3=substring;query_format=advanced;field0-0-3=status_whiteboard;short_desc_type=allwordssubstr;field0-0-2=short_desc;type0-0-0=substring;field1-0-0=product;type0-0-2=substring;field1-0-1=component。因此,您可以在那里创建一个工单。 - VonC
4
@darren 不是的。Eclipse有自己的编译器,完全独立于javac。在Stack Overflow上搜索“eclipse javac”,你会发现有许多情况(特别是与泛型相关的情况)它们的行为不同。 - Tyler
3
@aioobe - 你说得对,它们太聪明了。他们以一种合理的方式实现了它,即使语言定义不合理也是如此。抛出异常而不是转到默认值绝对是愚蠢的。现在我必须用 try/catch 包装一个 NullPointerException,在 catch 中做与默认值相同的事情。编辑:实际上,我会插入一个 null 检查,这比等待捕获异常更快,特别是因为它是预期的。 - ArtOfWarfare

4

当然。如果我们查看Java语言规范的第14.11章,它明确说明了(在“讨论”部分):

禁止使用null作为switch标签,以防止编写永远无法执行的代码。如果switch表达式是引用类型,例如装箱的原始类型或枚举类型,则如果表达式在运行时评估为null,则会发生运行时错误。


1
我可以想象一个更好的规则:1.允许一个没有被 default 覆盖的 null switch 标签,2.当表达式不是基本类型且缺少 null 标签时发出警告/错误,3.享受更短的代码和在 switch 中零 NPE 风险。 - maaartinus

1

是的。根据JLS,这是一个bug:

如果switch表达式是引用类型,例如装箱的原始类型或枚举类型,则如果表达式在运行时评估为null,则会发生运行时错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接