Java 枚举更改与添加新异常。需要重新编译吗?

Java 枚举更改与添加新异常。需要重新编译吗?,java,exception,enums,Java,Exception,Enums,这个问题基于“函数”一章“Error.java依赖磁铁”一节 作者声称,所有导入并使用此enum的类都必须重新编译,以防发生更改。这与添加新的例外衍生品相反。在后一种情况下,无需重新编译 但是,如果您通过添加新的派生异常,在更改包后立即使用包含派生异常的包,那么如果您的代码依赖于该包,则需要重新编译,这不是吗?或者,如果代码实际使用了一个新的派生异常,您是否只需要重新编译代码?本段还有一点内容。我将引用: 像这样的类是一个依赖磁铁;许多其他类必须导入并使用它们。因此,当错误枚举更改时,所有其他类

这个问题基于“函数”一章“Error.java依赖磁铁”一节

作者声称,所有导入并使用此
enum
的类都必须重新编译,以防发生更改。这与添加新的
例外
衍生品相反。在后一种情况下,无需重新编译


但是,如果您通过添加新的派生
异常
,在更改包后立即使用包含派生异常的包,那么如果您的代码依赖于该包,则需要重新编译,这不是吗?或者,如果代码实际使用了一个新的派生异常,您是否只需要重新编译代码?

本段还有一点内容。我将引用:

像这样的类是一个依赖磁铁;许多其他类必须导入并使用它们。因此,当
错误
枚举更改时,所有其他类都需要重新编译和重新部署

这起初读起来令人困惑,但事后看来是有道理的

枚举中声明了保证的枚举常量;向枚举中添加另一个值需要进行另一次编译才能获取该更改。这适用于使用枚举的所有类,即使它们不使用该值

另一方面,如果您声明了几个独立的
异常
派生类,如果您的特定类不需要特定的
异常
,则不需要重新编译它来使用它

这与你的下一个问题有关:

…[I]如果您通过添加新的派生的
异常来更改包含派生异常的包,那么如果您的代码依赖于该包,那么是否需要重新编译

这与我在Java程序中看到的一种更常见的趋势有关(与J1中标识的代码气味相冲突):除非您需要包中的所有内容,否则不要导入整个包


我怀疑在包级依赖性方面是否有实质性的区别;我找不到任何证据表明Java有条件地编译选择类,但我也不认为它会重新编译您的类,只是为了包含您未使用的异常。

这一段还有一点。我将引用:

像这样的类是一个依赖磁铁;许多其他类必须导入并使用它们。因此,当
错误
枚举更改时,所有其他类都需要重新编译和重新部署

这起初读起来令人困惑,但事后看来是有道理的

枚举中声明了保证的枚举常量;向枚举中添加另一个值需要进行另一次编译才能获取该更改。这适用于使用枚举的所有类,即使它们不使用该值

另一方面,如果您声明了几个独立的
异常
派生类,如果您的特定类不需要特定的
异常
,则不需要重新编译它来使用它

这与你的下一个问题有关:

…[I]如果您通过添加新的派生的
异常来更改包含派生异常的包,那么如果您的代码依赖于该包,那么是否需要重新编译

这与我在Java程序中看到的一种更常见的趋势有关(与J1中标识的代码气味相冲突):除非您需要包中的所有内容,否则不要导入整个包


我怀疑在包级依赖性方面是否有实质性的区别;我找不到任何证据表明Java有条件地编译选择类,但我也不相信它会重新编译您的类,只是为了包含您没有使用的异常。

Java在二进制兼容性方面非常聪明,因为它使用方法签名等,所以在大多数情况下,兼容性很容易维护。您可以添加子类并将它们传递到接受其超类的方法中,即使这些方法没有使用存在的子类进行编译,您也可以将方法添加到类中并添加接口供它们实现,并且二进制兼容性在大多数情况下仍然是一样的。对于枚举,情况并非如此。枚举经常使用
.ordinal()
方法(编译时)来执行某些任务(有关字节码的更多信息,请参见下文),因此,当类发生更改时,可能需要重新编译


Makoto的回答很好地涵盖了概念性内容,但直接原因与
javac
生成的字节码有关

以下班级:

public class TestEnums {

    public static void main(String[] args) throws Throwable {
        Matter matter = Matter.SOLID;
        switch (matter) {
            case SOLID:
                System.out.println("a");
                break;
            case LIQUID:
                System.out.println("b");
                break;
            case GAS:
                System.out.println("c");
                break;
        }
    }

    private enum Matter {
        SOLID,
        LIQUID,
        GAS
    }

}
生成以下字节码(在主方法中):

为了避免字节码的冗长,switch语句的主要逻辑以表开关的形式出现:

INVOKEVIRTUAL TestEnums$Matter.ordinal ()I
IALOAD
TABLESWITCH
  1: L2
  2: L3
  3: L4
  default: L5
如果查看,您会看到开关调用.ordinal()方法来确定要转到哪个值,因此如果在枚举的开头插入一个值:

private enum Matter {
    PLASMA, //Lying elementary school science teachers don't tell you about this one
    SOLID,
    LIQUID,
    GAS
}

序号已更改。由于字节码在处理枚举时经常使用序数方法,因此可能需要重新编译代码以保持与枚举的兼容性。

Java在二进制兼容性方面非常聪明,因为它使用方法签名等,所以在大多数情况下,兼容性很容易保持。您可以添加子类并将它们传递到接受其超类的方法中,即使这些方法没有使用存在的子类进行编译,您也可以将方法添加到类中并添加接口供它们实现,并且二进制兼容性在大多数情况下仍然是一样的。对于枚举,情况并非如此。枚举通常使用
.ordinal()
方法(当
INVOKEVIRTUAL TestEnums$Matter.ordinal ()I
IALOAD
TABLESWITCH
  1: L2
  2: L3
  3: L4
  default: L5
private enum Matter {
    PLASMA, //Lying elementary school science teachers don't tell you about this one
    SOLID,
    LIQUID,
    GAS
}