日食错误?仅使用默认情况打开 null

2022-01-19 00:00:00 null enums switch-statement java eclipse

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

公共类 SwitchingOnAnull {枚举 X { ,;}公共静态无效主要(字符串[]参数){X x = 空;开关(x){默认值:System.out.println("Hello world!");}}}

当使用 Eclipse 编译和运行时,它会打印 "Hello world!" 并正常退出.

使用 javac 编译器,这会按预期抛出 NullPointerException.

那么 Eclipse Java 编译器有 bug 吗?

解决方案

这是一个错误.这是根据 Java 语言规范,第 3 版为 switch 语句指定的行为:

JLS 14.11 开关 声明

<块引用>

SwitchStatement:switch ( 表达式 ) SwitchBlock

当执行 switch 语句时,首先评估 Expression.如果 Expression 的计算结果为 null,则抛出 NullPointerException 并且整个 switch 语句因此突然完成.

显然 Eclipse 中的错误与 default case 或 enum 完全无关.

公共类 SwitchingOnAnull {公共静态无效主要(字符串[]参数){java.math.RoundingMode x = null;开关(x){};开关((整数)空){};开关((字符)空){默认值:System.out.println("我有阳光!");}}}

上面的代码在(至少某些版本的)Eclipse 上编译并正常"运行.当使用 javac 编译时,每个单独的 switch 都会抛出 NullPointerException,这正是规范要求的.

<小时>

原因

这里是在Eclipse下编译时的javap -c SwitchingOnAnull:

编译自SwitchingOnAnull.java"公共类 SwitchingOnAnull 扩展 java.lang.Object{公共 SwitchingOnAnull();代码:0:aload_01:调用特殊#8;//方法java/lang/Object."<init>":()V4:返回公共静态无效主(java.lang.String[]);代码:0: 常量_null1:astore_12:获取静态#16;//字段 java/lang/System.out:Ljava/io/PrintStream;5:最不发达国家#22;//String 我有阳光!7:调用虚拟#24;//方法java/io/PrintStream.println:(Ljava/lang/String;)V10:返回}

Eclipse 编译器似乎完全摆脱了整个 switch 结构.不幸的是,这种优化违反了语言规范.

<小时>

官方说法

该错误已提交并分配修复.

<块引用>

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

我们在优化上过于激进.

为:

 switch((Integer) null) {};

我们优化了整个 switch 语句,当我们至少应该评估表达.

我去看看.

3.6.1 的候选人.

另见

  • 错误 314830 - 打开 null 表达式不会'不总是抛出 NullPointerException

I was experimenting with enum, and I found that the following compiles and runs fine on Eclipse (Build id: 20090920-1017, not sure exact compiler version):

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

When compiled and run with Eclipse, this prints "Hello world!" and exits normally.

With the javac compiler, this throws a NullPointerException as expected.

So is there a bug in Eclipse Java compiler?

解决方案

This is a bug. Here's the specified behavior for a switch statement according to the Java Language Specification, 3rd Edition:

JLS 14.11 The switch Statement

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.

Apparently the bug in Eclipse has nothing to do with default case or enum at all.

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!");
        }       
    }
}

The above code compiles and runs "fine" on (at least some version of) Eclipse. Each individual switch throws a NullPointerException when compiled with javac, which is exactly as the specification mandates.


The cause

Here's javap -c SwitchingOnAnull when compiled under Eclipse:

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

}

It seems that the Eclipse compiler gets rid of the entire switch constructs entirely. Unfortunately this optimization breaks the language specification.


The official words

The bug has been filed and assigned for fix.

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.

See also

  • Bug 314830 - Switching on a null expression doesn't always throw NullPointerException

相关文章