此问题基于Clean Code,章节“函数”,“The Error.java依赖磁铁”。
public enum Error {
OK,
INVALID,
NO_SUCH
}
作者声称,在发生更改时,必须重新编译导入和使用此enum
的所有类。这与添加新的Exception
衍生物相反。在后一种情况下,据说不需要重新编译。
但是,如果您通过添加新的派生Exception
来更改包,那么如果您使用包含派生异常的包,那么如果您的代码依赖于它将需要的包被重新编译?或者,如果代码实际上使用了一个新派生的Exceptions,那么代码是否只需要重新编译?
答案 0 :(得分:3)
这一段还有一点点。我引用:
像这样的类是依赖磁铁;许多其他类必须导入和使用它们。因此,当Error
枚举更改时,所有其他类都需要重新编译和重新部署。
这一开始令人困惑,但事后才有意义。
enum保证在其中声明枚举常量;在枚举中添加另一个值需要另外编译才能获取该更改。对于使用枚举的所有类都是如此,即使他们没有使用该值。
另一方面,如果你声明了几个独立的Exception
派生类,如果你的特定类不需要那个特定的Exception
,它就不需要重新编译它利用它。
这与你的下一个问题有关:
... [我]知道,如果您通过添加新的派生
Exception
更改软件包,则使用包含派生例外的软件包,如果您的代码依赖于在包上它需要重新编译?
这与我在Java程序中看到的更常见的趋势(以及与J1中标识的代码气味冲突)有关:除非您需要包中的所有内容,否则不要导入整个包。 Your imports may suddenly use the same class name to mean completely different things.
我怀疑在包级别依赖方面存在任何实质性差异;我无法找到任何证据表明Java有条件地编译选择的类,但我也不相信它会重新编译你的课程只是为了包含你的例外情况。也没有使用。
答案 1 :(得分:1)
由于它使用方法签名等方式,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
}
}
生成以下字节码(在main方法中):
public static main([Ljava/lang/String;)V throws java/lang/Throwable
L0
LINENUMBER 27 L0
GETSTATIC TestEnums$Matter.SOLID : LTestEnums$Matter;
ASTORE 1
L1
LINENUMBER 28 L1
GETSTATIC TestEnums$1.$SwitchMap$TestEnums$Matter : [I
ALOAD 1
INVOKEVIRTUAL TestEnums$Matter.ordinal ()I
IALOAD
TABLESWITCH
1: L2
2: L3
3: L4
default: L5
L2
LINENUMBER 30 L2
FRAME APPEND [TestEnums$Matter]
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
LINENUMBER 31 L6
GOTO L5
L3
LINENUMBER 33 L3
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "b"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L7
LINENUMBER 34 L7
GOTO L5
L4
LINENUMBER 36 L4
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "c"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
LINENUMBER 39 L5
FRAME SAME
RETURN
L8
LOCALVARIABLE args [Ljava/lang/String; L0 L8 0
LOCALVARIABLE matter LTestEnums$Matter; L1 L8 1
MAXSTACK = 2
MAXLOCALS = 2
越过字节码的详细程度,switch语句的主要逻辑以TableSwitch的形式出现:
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
}
序数改变了。由于字节码在操作枚举时使用了序数方法,因此可能必须重新编译代码以保持与枚举的兼容性。