如何将switch-statement中的这个值取消初始化?

时间:2016-09-25 00:22:04

标签: java compiler-errors switch-statement

我有这段代码:

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
}

为什么编译器会产生这些错误?

3 个答案:

答案 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);