Что такое использование по умолчанию, когда переключатель предназначен для перечисления?
Предположим, у меня есть перечислимый Color
С 2 возможными значениями: RED
и BLUE
:
public enum Color {
RED,
BLUE
}
теперь предположим, что у меня есть оператор switch для этого перечисления, где у меня есть код для обоих возможных значений:
Color color = getColor(); // a method which returns a value of enum "Color"
switch (color) {
case RED:
...
break;
case BLUE:
...
break;
default:
break;
}
так как у меня есть блок кода для обоих возможных значений перечисления, что такое использование default
в приведенном выше коде?
Я должен бросить исключение, если код каким-то образом достигает default
блок такой?
Color color = getColor(); // a method which returns a value of enum "Color"
switch (color) {
case RED:
...
break;
case BLUE:
...
break;
default:
throw new IllegalArgumentException("This should not have happened");
}
13 ответов:
Это хорошая практика, чтобы бросить исключение, как вы показали во втором примере. Вы улучшаете ремонтопригодность кода не быстро.
в этом случае это будет означать, что если вы позже (возможно, годы спустя) добавите значение перечисления, и оно достигнет оператора switch, вы сразу обнаружите ошибку.
Если бы значение по умолчанию не было установлено, код, возможно, выполнялся бы даже с новым значением перечисления и, возможно, был бы нежелательным поведение.
другие ответы верны, говоря, что вы должны реализовать
default
ветвь, которая выдает исключение, в случае, если новое значение будет добавлено к вашему перечислению в будущем. Тем не менее, я бы сделал еще один шаг и задался вопросом, почему вы даже используетеswitch
заявление в первую очередь.В отличие от таких языков, как C++ и C#, Java представляет значения Enum как фактические объекты, что означает, что вы можете использовать объектно-ориентированное программирование. Предположим, что целью вашего метода является чтобы предоставить значение RGB для каждого цвета:
switch (color) case RED: return "#ff0000"; ...
ну, возможно, если вы хотите, чтобы каждый цвет имел значение RGB, вы должны включить это как часть его описания:
public enum Color { RED("#FF0000"), BLUE("#0000FF"); String rgb; public Color(String rgb) { this.rgb = rgb; } public getRgb() { return this.rgb; } }
таким образом, если вы добавите новый цвет позже, вы в значительной степени вынуждены предоставить значение RGB. Это даже более быстрый отказ, чем другой подход, потому что вы потерпите неудачу во время компиляции, а не во время выполнения.
обратите внимание, что вы можете делать еще более сложные вещи, если вам нужно, в том числе наличие каждого цвета обеспечивает свою собственную пользовательскую реализацию абстрактного метода. Перечисления в Java действительно мощные и объектно-ориентированные, и в большинстве случаев я обнаружил, что могу избежать необходимости
switch
на них в первую очередь.
полнота времени компиляции случаев переключения не гарантирует завершения времени выполнения.
класс с оператором switch, скомпилированным против более старой версии enum, может быть выполнен с более новой версией enum (с большим количеством значений). Это распространенный случай с зависимостями библиотек.
по таким причинам компилятор считает
switch
безdefault
случае неполным.
в небольших программах нет практической пользы для этого, но подумайте о сложной системе, которая копья среди большого количества файлов и разработчиков - если вы определяете
enum
в одном файле и использовать его в другом, а позже кто-то добавляет значениеenum
без обновленияswitch
утверждение, Вы найдете его очень полезным...
Да, вы должны сделать это. Вы можете изменить
enum
но не изменитьswitch
. В будущем это приведет к ошибкам. Я думаю, чтоthrow new IllegalArgumentException(msg)
- это хорошая практика.
Если вы охватили все возможности с различными
cases
иdefault
не может произойти, это классический вариант использования для утверждения:Color color = getColor(); // a method which returns a value of enum "Color" switch (color) { case RED: // ... break; case BLUE: // ... break; default: assert false; // This cannot happen // or: throw new AssertionError("Invalid Colors enum"); }
когда константы перечисления слишком много, и вам нужно обрабатывать только для нескольких случаев, то
default
будет обрабатывать остальные константы.кроме того, константы перечисления являются ссылками, если ссылка еще не установлена, или
null
. Возможно, Вам тоже придется иметь дело с такими случаями.
Да, это мертвый код, пока кто-то не добавит значение в перечисление, что заставит ваш оператор switch следовать принципу "fail fast" (https://en.wikipedia.org/wiki/Fail-fast)
Это может относиться к этому вопросу:как обеспечить полноту в коммутаторе перечисления во время компиляции?
чтобы удовлетворить IDE и другие статические линтеры, я часто оставляю случай по умолчанию в качестве no-op вместе с комментарием, таким как
// Can't happen
или// Unreachable
т. е., если коммутатор делает типичную вещь обработки всех возможных значений перечисления, либо явно, либо через провалы, то по умолчанию, вероятно, ошибка программиста.
в зависимости от приложения, я иногда ставлю утверждение в случае, чтобы защитить от ошибки программиста во время разработки. Но это ограниченное значение в коде доставки (если вы не отправляете с включенными утверждениями.)
опять же, в зависимости от ситуации я мог бы быть убежден, чтобы бросить ошибку, так как это действительно неисправимая ситуация-ничего пользователь не может сделать, чтобы исправить то, что, вероятно, ошибка программиста.
помимо возможного будущего расширения перечисления, на которое указывали многие, когда-нибудь кто-то может "улучшить" yout
getColor()
или переопределить его в производном классе, и пусть он возвращает неверное значение. Конечно, компилятор должен поймать это, если кто-то явно не вызывает небезопасное приведение типов...но плохие вещи просто случаются, и это хорошая практика, чтобы не оставить любой неожиданный
else
илиdefault
путь без охраны.
Я удивлен, что никто больше не упоминал об этом. Вы можете привести int к перечислению, и он не будет бросать только потому, что значение не является одним из перечисленных значений. Это означает (среди прочего), что компилятор не может сказать, что все значения перечисления находятся в коммутаторе.
даже если вы пишете свой код правильно, это действительно происходит при сериализации объектов, содержащих перечисления. Будущая версия может добавить к перечислению, и ваш код задохнется при чтении его обратно, или кто-то хочет создать mayhem может hexedit новое значение В. В любом случае, выключение переключателя редко делает правильные вещи. Итак, мы бросаем по умолчанию, если мы не знаем лучше.
вот как я бы справился с этим, рядом
NULL
значение, которое приведет к исключению нулевого указателя, которое вы можете обработать.если
Color color
неnull
, это должен быть один из синглетов вenum Color
, если вы назначите любую ссылку на объект, который не является одним из них, это вызовет ошибку времени выполнения.поэтому мое решение заключается в учете значений, которые не поддерживаются.