Почему по умолчанию требуется для включения перечисления? - PullRequest
37 голосов
/ 16 февраля 2011

Как правило, по умолчанию не требуется в инструкции switch.Однако в следующей ситуации код успешно компилируется только тогда, когда я раскомментирую оператор по умолчанию.Кто-нибудь может объяснить, почему?

public enum XYZ {A,B};
public static String testSwitch(XYZ xyz)
{
    switch(xyz)
    {
    case A:
        return "A";
    case B:
    //default:
        return "B";
    }
}

Ответы [ 7 ]

43 голосов
/ 16 февраля 2011

Причина, по которой вы должны раскомментировать default, заключается в том, что ваша функция сообщает, что она возвращает String, но если у вас есть только case метки, определенные для A и B, то функция не будет вернуть значение, если вы передадите что-нибудь еще. Java требует, чтобы все функции, которые заявляют, что они возвращали значение, действительно возвращали значение на всех возможных путях управления, и в вашем случае компилятор не убежден, что все возможные входные данные имеют возвращенное значение.

Я полагаю (и не уверен в этом), что причина этого в том, что даже если вы охватите все ваши enum случаи, в некоторых случаях код все равно может не работать. В частности, предположим, что вы компилируете код Java, содержащий этот оператор switch (который работает просто отлично), а затем измените enum так, чтобы теперь появилась третья константа - скажем, C - но вы не перекомпилируете код с оператором switch. Теперь, если вы попытаетесь написать код Java, который использует ранее скомпилированный класс и передает C в этот оператор, то у кода не будет возвращаемого значения, что нарушает контракт Java, который все функции всегда возвращают значения.

Говоря более технически, я думаю, что настоящая причина в том, что верификатор байт-кода JVM всегда отклоняет функции, в которых есть некоторый путь управления, выходящий за пределы функции (см. Раздел 4.9.2 спецификации JVM) ) и поэтому, если код должен был скомпилироваться, он все равно будет отклонен JVM во время выполнения. Поэтому компилятор выдает ошибку, сообщая о существовании проблемы.

40 голосов
/ 16 февраля 2011

Я думаю, это объясняется правилами определенного присваивания JLS для операторов switch ( JLS 16.2.9 ), в которых указано следующее:

"V[un] присваивается после оператора переключения, если все следующее верно:

  • Либо в блоке коммутатора есть метка по умолчанию, либо V назначается [un] послевыражение переключения.

Если мы затем применим это к условному V, которое является возвращаемым значением метода, мы можем видеть, что если default нетветвь, значение было бы условно не назначено.

ОК ... Я экстраполирую определенные правила присваивания для покрытия возвращаемых значений, и, возможно, они не. Но тот факт, что я не смог найтичто-то более прямое в спецификации не означает, что его там нет: -)


Есть еще одна (более веская) причина, по которой компилятор должен выдавать ошибку.двоичные правила совместимости для enum ( JLS 13.4.26 ), которые устанавливаютследующее:

«Добавление или переупорядочение констант из типа enum не нарушит совместимость с уже существующими двоичными файлами.»

Так как же это сделатьприменять в этом случае?Хорошо предположим, что компилятору было позволено сделать вывод, что пример оператора switch OP всегда возвращал что-то.Что произойдет, если программист теперь изменит enum, чтобы добавить дополнительную константу?Согласно правилам двоичной совместимости JLS, мы не нарушили двоичную совместимость.Тем не менее, метод, содержащий оператор switch, теперь может (в зависимости от его аргумента) возвращать неопределенное значение.То, что не может быть допустимым, поэтому переключатель должен быть ошибкой компиляции.


В Java 12 были введены усовершенствования для коммутатора, включающие коммутаторвыражения.Это сталкивается с той же проблемой с перечислениями, которые изменяются между временем компиляции и временем выполнения.Согласно JEP 354 они решают эту проблему следующим образом:

Случаи выражения switch должны быть исчерпывающими;для всех возможных значений должна быть соответствующая метка переключателя.(Очевидно, что операторы switch не обязательно должны быть исчерпывающими.)

На практике это обычно означает, что требуется предложение по умолчанию;однако в случае выражения переключения перечисления, которое охватывает все известные константы, компилятор вставляет предложение по умолчанию, чтобы указать, что определение перечисления изменилось между временем компиляции и временем выполнения.Использование неявной вставки предложения по умолчанию делает код более надежным;теперь, когда код перекомпилирован, компилятор проверяет, что все случаи обрабатываются явно.Если бы разработчик вставил явное предложение по умолчанию (как в сегодняшнем случае), возможная ошибка была бы скрыта.

Единственное, что не совсем ясно, - это то, что фактически делает неявное предложение по умолчанию.Я предполагаю, что это приведет к непроверенному исключению.(На данный момент JLS для Java 12 не обновлялся для описания новых выражений переключателей.)

7 голосов
/ 16 февраля 2011

Как уже было сказано, вам нужно вернуть значение, и компилятор не предполагает, что перечисление не может измениться в будущем. Например. Вы можете создать другую версию enum и использовать ее без перекомпиляции метода.

Примечание: есть третье значение для xyz, которое является нулевым.

public static String testSwitch(XYZ xyz) {
    if(xyz == null) return "null";
    switch(xyz){
    case A:
        return "A";
    case B:
        return "B";
    }
    return xyz.getName();
}

Это тот же результат, что и

public static String testSwitch(XYZ xyz) {
     return "" + xyz;
}

Единственный способ избежать возврата - вызвать исключение.

public static String testSwitch(XYZ xyz) {
    switch(xyz){
    case A:
        return "A";
    case B:
        return "B";
    }
    throw new AssertionError("Unknown XYZ "+xyz);
}
2 голосов
/ 19 марта 2019

В Java 12 вы можете использовать функцию выражения переключателя предварительного просмотра ( JEP-325 ) следующим образом:

public static String testSwitch(XYZ xyz) {
    return switch (xyz) {
        case A -> "A";
        case B -> "B";
    };
}

и регистр по умолчанию не требуется, если вы обрабатываете все значения перечисления в switch.

Обратите внимание, что для использования функции предварительного просмотра вам необходимо передать --enable-preview --source 12 опций javac и java

1 голос
/ 16 февраля 2011

Существует договор, что этот метод имеет для возврата строки, если он не выдает исключение.И каждый раз не ограничивается теми случаями, когда значение xyz равно XVZ.A или XYZ.B.

Вот еще один пример, в котором оно obviuos , что код будет работать правильно, но там, где у нас есть ошибка времени компиляции по той же причине:

public boolean getTrue() {
  if (1 == 1) return true;
}

Это неправда, что вам нужно добавить оператор по умолчанию, этоверно, что вы должны вернуть значение в любое время.Поэтому добавьте оператор по умолчанию или оператор возврата после блока переключателей.

0 голосов
/ 16 февраля 2011
default: throw new AssertionError();
0 голосов
/ 16 февраля 2011

Поскольку компилятор не может догадаться, что в enum есть только два значения, и вынуждает вас возвращать значение из метода.(Однако я не знаю, почему он не может догадаться, может быть, это что-то с отражением).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...