我有这段代码:
int putReferenceItem(Reference reference) {
switch (reference.type) {
case CLASS:
return putStringItem(CONSTANT_Class, reference.name);
case FIELD:
case METHOD:
case INTERFACE_METHOD:
int itemTag;
switch (reference.type) {
case FIELD:
itemTag = CONSTANT_Fieldref;
break;
case METHOD:
itemTag = CONSTANT_Methodref;
break;
case INTERFACE_METHOD:
itemTag = CONSTANT_InterfaceMethodref;
break;
}
return putStringItem(itemTag, reference.owner, reference.name, reference.descriptor);
case HANDLE:
return put(CONSTANT_MethodHandle, reference.kind, 0, 0, reference.owner, reference.name, reference.descriptor);
case TYPE:
return putStringItem(CONSTANT_MethodType, reference.descriptor);
default:
throw new Error("Unreachable code.");
}
}
在线,
return putStringItem(itemTag, reference.owner, reference.name, reference.descriptor);
编译器抱怨itemTag
的值可能尚未初始化。对我来说,嵌套的switch语句总是初始化itemTag
的值似乎很明显,所以我很难理解为什么会产生这个错误。编译器是不是很复杂,不足以评估嵌套的switch语句,或者itemTag
实际上是否可能未初始化?
我目前的解决方案是简单地添加一个抛出Error
的默认案例:
switch (reference.type) {
case FIELD:
itemTag = CONSTANT_Fieldref;
break;
case METHOD:
itemTag = CONSTANT_Methodref;
break;
case INTERFACE_METHOD:
itemTag = CONSTANT_InterfaceMethodref;
break;
default:
throw new Error("Unreachable code.");
}
然而,我觉得这是一个非常难看的解决方案。 (更新:我刚刚意识到我可以用case INTERFACE_METHOD:
替换default:
并且仍然可以获得相同的行为而不会有丑陋。我的问题仍然存在。)
您可能还注意到我稍后在代码中重用了此解决方案,以避免返回值。这个switch语句有ReferenceType
中定义的每个常量的大小写,所以我不完全确定代码如何丢失return
语句。
public enum ReferenceType {
CLASS,
FIELD,
METHOD,
INTERFACE_METHOD,
HANDLE,
TYPE
}
为什么编译器会产生这些错误?
答案 0 :(得分:1)
编译器不会查看switch-statements内部以查看是否初始化变量。如果它未在当前范围内初始化(例如,在创建变量的switch语句之外),则会出现此错误。这样做的目的是保护您免受呼叫" null"当您稍后在代码中调用变量时。
如果您知道在switch语句中始终将其设置为正确的值,那么使用任意值初始化变量应该是安全的。这是否是最优雅的解决方案有争议,但它应该解决您的问题。我认为这比处理你所犯的错误要好。
编辑:
关于问题的后半部分,我认为创建一个变量以在正确的范围内返回可以解决您的问题。例如。如果初始化变量(再次使用任意值),则可以在switch语句中使用正确的值设置它,然后在方法的末尾返回变量。那应该摆脱错误。
答案 1 :(得分:1)
这是有问题的代码
int itemTag;
switch (reference.type) {
case FIELD:
itemTag = CONSTANT_Fieldref;
break;
case METHOD:
itemTag = CONSTANT_Methodref;
break;
case INTERFACE_METHOD:
itemTag = CONSTANT_InterfaceMethodref;
break;
}
如果itemTag
是这三个reference.type
值中的一个,则显式enum
已初始化。但是其他两个呢?在编写的代码中,如果它们发生,switch
语句将不会初始化itemTag
。
现在你(程序员)可能会推断出其他两种情况不会发生。但编译器无法做到。为了进行推导,编译器需要深入了解Reference
类的语义,以及如何使用它。它根本做不到。
解决方案是将上述内容更改为:
int itemTag;
switch (reference.type) {
case FIELD:
itemTag = CONSTANT_Fieldref;
break;
case METHOD:
itemTag = CONSTANT_Methodref;
break;
case INTERFACE_METHOD:
itemTag = CONSTANT_InterfaceMethodref;
break;
default:
throw AssertionError("impossible reference type");
}
现在,编译器可以推断出itemTag
已初始化为所有可能的方式,您可以使用return
语句来使用该变量。
顺便提一下,以下内容不是有效的解决方案。
int itemTag;
switch (reference.type) {
case FIELD:
itemTag = CONSTANT_Fieldref;
break;
case METHOD:
itemTag = CONSTANT_Methodref;
break;
case INTERFACE_METHOD:
itemTag = CONSTANT_InterfaceMethodref;
break;
case CLASS: case HANDLE: case TYPE:
throw AssertionError("impossible reference type");
}
这不是解决方案的原因是Java中的类可以按任何顺序重新编译。假设编译了上面的代码,然后你更改了enum
声明以添加一个新的常量,并重新编译它。现在在运行时上面的switch语句不再涵盖所有可能的情况,这意味着itemTag
可以使用而不进行初始化。哎呀!
因此编译器会抱怨上面的代码,说itemTag
可能并不总是被初始化。
奇怪......而且只是......但由于上述原因,这是必要的。
答案 2 :(得分:0)
嗯,对我而言,价值将被初始化并不是那么明显。
查看我插入<here>
的位置。现在假设当执行处于此时,另一个线程开始并将reference.type
更改为HANDLE
并且瞧,您有一个值switch
中没有分支的值,该值将是初始化。
case INTERFACE_METHOD:
int itemTag;
// <here>
switch (reference.type) {
case FIELD:
itemTag = CONSTANT_Fieldref;
break;
case METHOD:
itemTag = CONSTANT_Methodref;
break;
case INTERFACE_METHOD:
itemTag = CONSTANT_InterfaceMethodref;
break;
}
return putStringItem(itemTag, reference.owner, reference.name, reference.descriptor);
除此之外,如果您不想覆盖reference.type更改的情况,为什么还有一个内部开关?在外部交换机中,代码不会更好吗?
case INTERFACE_METHOD:
return putStringItem(CONSTANT_InterfaceMethodref, reference.owner, reference.name, reference.descriptor);