Когда использовать оператор switch в Java
Я ценю, что все, что может быть сделано в статусе переключения, может выполняться с помощью инструкции if else.
Но существуют ли стилистические правила для того, когда нужно использовать коммутатор, а не в противном случае.
Ответы
Ответ 1
Хорошо, switch
во многих случаях "легче", чем лестница if
/else if
, на мой взгляд. В принципе, у вас не так много синтаксиса с фигурными скобками и скобками на пути вашего кода. При этом switch
наследует синтаксис Си. Это означает, что у вас есть break
и только одна область переменных, если вы не вводите новые блоки.
Тем не менее, компилятор может оптимизировать операторы switch
в таблице поиска и выполнять проверку времени компиляции для литералов при работе с перечислениями. Поэтому я бы предположил, что обычно использовать switch
над if
/else if
, если вы имеете дело с числовыми или перечисляемыми типами.
Ответ 2
У коммутатора есть одно преимущество, когда дело доходит до ясности:
switch (i) {
case 1:
// do something
break;
case 2:
case 4:
// do something
break;
case 5:
// do something
break;
}
Если код для 2 и 4 идентичен, он может быть более понятным, чем:
if ( i == 1 ) {
// do something
}
if ( (i == 2) || (i == 4) ) {
// do something
}
if ( (i == 5 ) {
// do something
}
Вам также легче (или другому программисту) разделить случаи 2 и 4.
Ответ 3
Я использую операторы switch для перечислений, более читаемо, если if if if if else if. Однако вы должны стараться избегать таких проверок в дизайне OO.
Ответ 4
Вы используете оператор switch, когда вы включаете разные значения типов примитивов /enum/wrapper. (И не все типы примитивов/оболочек, только поддерживаемые - байты, короткие, char, int).
Если/else обрабатывает остальные.
Например, более эстетично говорить:
int i = getValueOfI();
switch (i) {
case 1:
// do something
break;
case 2:
// do something
break;
и др.
чем
if (i == 1) {
} else if (i == 2) {
}...
для большого числа случаев. Но вы не можете включать строки, значения функций или сложные условия, поэтому, если вам нужно что-то сделать, тогда вы застряли с if/else.
Ответ 5
Лично я использую, чтобы найти обе конструкции слишком процедурные.
Хотя это может рассматриваться как экзистентность OO, я использую для использования карты, содержащей экземпляры внутреннего интерфейса для каждого случая if.
Думаю, это позволяет улучшить изоляцию кода.
Однако, чтобы trully ответить на ваш вопрос, я использую только переключатели, когда у меня есть случаи, которые действительно перекрываются (я не использую инструкции break). К сожалению, это действительно не поддерживаемый кодовый блок.
Ответ 6
Я бы предложил простое правило:
Всегда используйте switch
, если у вас есть как минимум 2 варианта, чтобы различать, когда тип данных можно использовать для коммутатора, и когда все параметры имеют постоянные значения.
Есть три веские причины. Один, в большинстве случаев switch
быстрее, чем каскад if
/else
. Во-вторых, это делает код более понятным. Три, о так забытая дилемма break
- это меньшее зло, чем огромные каскады if
/else
, которые случайно ломаются, потому что кто-то забыл else
.
Java-виртуальные машины фактически поддерживают два разных типа коммутатора: команду tableswitch и инструкцию lookupswitch. Команда tableswitch генерируется компилятором, если все константы case
лежат в узком диапазоне, в противном случае он генерирует поисковый переключатель. Для больших операторов switch
со многими случаями таблицаwitch более эффективна, чем lookupswitch. Lookupswitch обычно реализуется в виде бинарного поиска.
Ответ 7
Есть два фактора для меня:
Считываемость и хотите ли вы решить что-то, используя диапазоны значений или условий (в операторах switch вы можете использовать только одно целое или перечислимое значение).
Ответ 8
прежде всего, оператор switch должен использоваться. Если переключатель основан на значении переменной, то он может использоваться. Если он основан на сложном логическом выражении AND/OR/NOT, которое меняется для каждого условия, то вы не можете использовать его вообще.
При этом, если это применимо, и есть как минимум 2 случая, я пользуюсь коммутатором. Это более легко расширяемое, удобное для чтения и проверки.
Ответ 9
Переключение и перечисление.
Если значение перечисления, которое вы тестируете, может быть законным null
по какой-либо причине, помещая его в оператор switch, генерирует исключение NullPointerException. Не смотря на байт-код, это несколько озадачивает, что он это сделает.
Объяснение: перечисления представляют собой синтаксический сахар, введенный в 1.5. Оператор switch по-прежнему работает с хорошими именами, но значения, которые он использует, являются ординалами, назначенными для перечисления. Чтобы получить порядковый номер, значение перечисления ДОЛЖНО быть не нулевым.
if statement
, с другой стороны, будет рад принять null
за значение перечисления и просто пропустить тест без NPE.
Ответ 10
Может быть, немного оффтопик, но если бы я ответил только на вопрос в названии, я бы сказал, что вы не должны использовать переключатель во всех ситуациях, когда случаи представляют состояния какого-либо объекта. В этих случаях шаблон состояния гораздо более приятный.
Ответ 11
Я всегда обнаружил, что оператор java-переключателя не настолько мощный, насколько мне нужно. В своем последний выпуск lambdaj реализует его с помощью умного использование укупорочного средства и приспособления Hamcrest.
Например, lambdaj Switcher позволяет реализовать шаблон стратегии. Предположим, вам нужно переключиться между тремя алгоритмами сортировки, основанными на некоторой характеристике сортируемого списка. В частности, предположим, что у нас есть алгоритм, специализированный для строк:
public List<String> sortStrings(List<String> list) {
// a sort algorithm suitable for Strings
}
другой, который хорошо работает с небольшими списками, имеющими не более 100 элементов:
public List<T> sortSmallList(List<T> list) {
// a sort algorithm suitable for no more than 100 items
}
и более общего назначения:
public List<String> sort(List<String> list) {
// a generic sort algorithm
}
Учитывая эти 3 метода сортировки, можно создать стратегию, которая выбирает наиболее подходящий из них следующим декларативным способом:
Switcher<List<T>> sortStrategy = new Switcher<List<T>>()
.addCase(having(on(List.class).get(0), instanceOf(String.class))),
new Closure() {{ of(this).sortStrings(var(List.class)); }})
.addCase(having(on(List.class).size(), lessThan(100))),
new Closure() {{ of(this).sortSmallList(var(List.class)); }})
.setDefault(new Closure() {{ of(this).sort(var(List.class)); }});
и отсортировать список с использованием наилучшего доступного алгоритма, вызвав коммутатор:
List<T> sortedList = sortStrategy.exec(list, list);
Ответ 12
Ответ зависит от того, что именно вы делаете, а также от распределения вариантов.
Если одно условие является доминантным, то if/тогда подходит.
if (i == 1){
//do something
}else if (i == 2){
// do something else
}
Если условия распределены равномерно, оптимизация в компиляторе обеспечит преимущество в производительности. Эта разница в производительности становится более выраженной по мере увеличения количества возможных вариантов.
switch (i) {
case 1:
// do something
break;
case 2:
// do something else
break;
....
case N:
// do yet something else
break;
}
Тем не менее, если производительность НЕ важна, пойдите, с которой вы когда-либо приближаетесь, вы предпочитаете как наиболее читаемый (поддерживаемый и простой для записи).
Если, с другой стороны, если ваш код находится в точке доступа, где производительность важна, вы должны пойти с коммутатором.
Для "чрезвычайно больших" условий пример Марио lambdaj-переключателя действительно классный и с осторожностью при инициализации приведет к очень высокой производительности. Это очень похоже на кодирование того, что генерирует оптимизатор. Я бы назвал "чрезвычайно большим", как если бы количество опций было большим или достаточно сложным, чтобы заставить его стоить все, что нужно, и стоит того, чтобы поддерживать путаницу, когда последующий разработчик пытается пройти через код. (Комментируйте свой код, почему у вас есть все это!).
Ответ 13
Если у вас слишком много условий для проверки аналогичного типа, вы можете переключиться на переключатель.
Ответ 14
Как и в случае с другими языками, такими как C или С++, операторы switch полезны, когда вы хотите сравнить данную переменную со списком возможных значений и выполнить действие в зависимости от этих значений. Это термины, чем выражения else.
Ответ 15
Я согласен с ответом x4u.
Кроме того, еще не упоминается еще один экземпляр, где я думаю, что лучше использовать блоки if
- else
, а не switch
: когда тестируются дополнительные условия в каждом из блоков case
с блоками if
. Я вижу эту смесь все время в существующем коде.
Например, я натолкнулся на этот код с switch
на строку type
и затем проверяет вторую строку extension
с помощью if
в обоих операторах case
.
public abstract class Temp {
boolean valid;
public Temp() {
String type = getType();
String extension = getFilenameExtension();
switch(type) {
case "Image File": {
if(!".jpg".equals(extension) && !".png".equals(extension)) {
warnWrongImageFormat();
valid = false;
}
break;
}
case "Zip File": {
if(!".zip".equals(extension)) {
warnWrongZipFormat();
valid = false;
}
break;
}
default: {
valid = true;
break;
}
}
}
abstract String getType();
abstract String getFilenameExtension();
abstract void warnWrongImageFormat();
abstract void warnWrongZipFormat();
}
Вместо этого гораздо проще и менее сложно уменьшить это до одного if
else
public abstract class Temp {
boolean valid;
public Temp() {
String type = getType();
String extension = getFilenameExtension();
valid = true;
if("Image File".equals(type) && !".jpg".equals(extension) && !".png".equals(extension)) {
warnWrongImageFormat();
valid = false;
}
else if("Zip File".equals(type) && !".zip".equals(extension)) {
warnWrongZipFormat();
valid = false;
}
}
abstract String getType();
abstract String getFilenameExtension();
abstract void warnWrongImageFormat();
abstract void warnWrongZipFormat();
}
Ответ 16
Коммутатор имеет два недостатка:
- он ограничен примитивными типами и перечислениями
- вам нужно запомнить "break", что может привести к не столь очевидным ошибкам.
Часто переключатель является признаком плохого дизайна OO, потому что вам лучше использовать полиморфизм.
Единственным возможным преимуществом коммутатора является то, что оно более читаемо. Но
switch (i) {
case 1:
// do something
break;
case 2:
// do something
break;
}
более читаемый, чем это:
if (i == 1)
//do something
else if (i == 2)
// do something else
Я бы сказал: нет! И у вас не было бы недостатков переключателя.
Мое предложение: постарайтесь избежать переключения.