Оператор переключения Java несколько случаев


118

Просто пытаюсь понять, как использовать несколько вариантов для оператора переключения Java. Вот пример того, что я пытаюсь сделать:

switch (variable)
{
    case 5..100:
        doSomething();
    break;
}

вместо того, чтобы делать:

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}

Есть идеи, если это возможно, или какая хорошая альтернатива?


12
Похоже, вы используете целые числа, поэтому я полагаю, если вы знаете, что ваши диапазоны имеют фиксированный размер, вы всегда можете использовать switch (variable / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Ответы:


80

К сожалению, в Java это невозможно. Вам придется прибегнуть к использованию if-elseоператоров.


1
@FunJavaCode AFAIK вы можете сделать это в vb.net. Вот пример
Bala R

1
@YuryLitvinov вы хотите расширить, почему вы считаете это неправильным?
Bala R

1
Мой ответ доказывает, что это неверно, это объектно-ориентированный подход и является известным и принятым шаблоном для решения этой точной проблемы среди других, требующих множества вложенных if/elseif/elseоператоров, независимо от языка.

1
Можно получить условие ИЛИ в случае SWITCH, используя эту ссылку ... пожалуйста, проверьте это: - stackoverflow.com/a/16706729/3946958
Равиндра Кушваха

4
братан его возможное использование может написать несколько case без использования break, а в конце case вы можете написать свою логику, например: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2 09

85

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

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}

50
Спрашивающий сказал: делай это «против» того, чтобы делать это. Он понимает, что то, что вы перечислили, действительно, он пытался сделать первое ВМЕСТО этого.
Блейн Маклоу

45
Извините, но я не понимаю, как перечисление 95 случаев подряд, относящихся к одному и тому же, - это решение чего-либо. Если бы я столкнулся с этим в любом коде, я бы выследил их, похитил, доставил лично в GLaDOS и надеялся, что она проведет для них самую смертоносную последовательность тестов, какую только сможет найти.
animuson

1
@animuson, кроме того, он получил 60 голосов за ... Я пришел сюда, потому что я не хотел делать ТОЧНО то, что он отвечает
killjoy

Голосование против не дает ответа на вопрос. , .heck, видимо, вопрос даже не читали для того, чтобы написать этот ответ.
iheanyi 06

50
public class SwitchTest {
    public static void main(String[] args){
        for(int i = 0;i<10;i++){
            switch(i){
                case 1: case 2: case 3: case 4: //First case
                    System.out.println("First case");
                    break;
                case 8: case 9: //Second case
                    System.out.println("Second case");
                    break;
                default: //Default case
                    System.out.println("Default case");
                    break;
            }
        }
    }
}

Вне:

Default case
First case
First case
First case
First case
Default case
Default case
Default case
Second case
Second case

Источник: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html


4
Это то же самое, что и часть его вопроса «против», которой он хотел избежать.
Bdoserror

2
Ответы только на код почти так же плохи, как ответы только на ссылки. На самом деле это совсем не ответы. Хороший ответ содержит объяснения и рассуждения и, возможно, некоторые источники или авторитетные источники.
Маркус

3
Это лучше, чем ничего, некоторые люди считают это лучшим ответом, простым и прямым
D4rWiNS

48

Может быть, не так элегантно, как некоторые предыдущие ответы, но если вы хотите добиться случаев переключения с несколькими большими диапазонами, просто заранее объедините диапазоны в один случай:

// make a switch variable so as not to change the original value
int switchVariable = variable;

//combine range 1-100 to one single case in switch
if(1 <= variable && variable <=100)
    switchVariable = 1;
switch (switchVariable) 
{ 
    case 0:
        break; 
    case 1:
        // range 1-100
        doSomething(); 
        break;
    case 101: 
        doSomethingElse(); 
        break;
    etc.
} 

11
Я бы не рекомендовал это. Если вы просто прогоните код, у вас будет интуиция, которая case 1означает variable == 1, что в конечном итоге приведет к путанице и большим неудобствам. Если вам нужно разместить в коде комментарии, чтобы сделать его читабельным, значит, вы сделали что-то не так, ИМХО.
kraxor 04

21

Один объектно - ориентированный вариант замены чрезмерно большой switchи if/elseконструкции является использование Chain of Responsibility Patternдля моделирования процесса принятия решений.

Схема цепочки ответственности

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

Вот пример реализации, которая также является Type Safe с использованием Generics.

import java.util.ArrayList;
import java.util.List;

/**
* Generic enabled Object Oriented Switch/Case construct
* @param <T> type to switch on
*/
public class Switch<T extends Comparable<T>>
{
    private final List<Case<T>> cases;

    public Switch()
    {
        this.cases = new ArrayList<Case<T>>();
    }

    /**
     * Register the Cases with the Switch
     * @param c case to register
     */
    public void register(final Case<T> c) { this.cases.add(c); }

    /**
     * Run the switch logic on some input
     * @param type input to Switch on
     */
    public void evaluate(final T type)
    {
        for (final Case<T> c : this.cases)
        {
            if (c.of(type)) { break; }
        }
    }

    /**
     * Generic Case condition
     * @param <T> type to accept
     */
    public static interface Case<T extends Comparable<T>>
    {
        public boolean of(final T type);
    }

    public static abstract class AbstractCase<T extends Comparable<T>> implements Case<T>
    {
        protected final boolean breakOnCompletion;

        protected AbstractCase()
        {
            this(true);
        }

        protected AbstractCase(final boolean breakOnCompletion)
        {
            this.breakOnCompletion = breakOnCompletion;
        }
    }

    /**
     * Example of standard "equals" case condition
     * @param <T> type to accept
     */
    public static abstract class EqualsCase<T extends Comparable<T>> extends AbstractCase<T>
    {
        private final T type;

        public EqualsCase(final T type)
        {
            super();
            this.type = type;
        }

        public EqualsCase(final T type, final boolean breakOnCompletion)
        {
            super(breakOnCompletion);
            this.type = type;
        }
    }

    /**
     * Concrete example of an advanced Case conditional to match a Range of values
     * @param <T> type of input
     */
    public static abstract class InRangeCase<T extends Comparable<T>> extends AbstractCase<T>
    {
        private final static int GREATER_THAN = 1;
        private final static int EQUALS = 0;
        private final static int LESS_THAN = -1;
        protected final T start;
        protected final T end;

        public InRangeCase(final T start, final T end)
        {
            this.start = start;
            this.end = end;
        }

        public InRangeCase(final T start, final T end, final boolean breakOnCompletion)
        {
            super(breakOnCompletion);
            this.start = start;
            this.end = end;
        }

        private boolean inRange(final T type)
        {
            return (type.compareTo(this.start) == EQUALS || type.compareTo(this.start) == GREATER_THAN) &&
                    (type.compareTo(this.end) == EQUALS || type.compareTo(this.end) == LESS_THAN);
        }
    }

    /**
     * Show how to apply a Chain of Responsibility Pattern to implement a Switch/Case construct
     *
     * @param args command line arguments aren't used in this example
     */
    public static void main(final String[] args)
    {
        final Switch<Integer> integerSwitch = new Switch<Integer>();
        final Case<Integer> case1 = new EqualsCase<Integer>(1)
        {
            @Override
            public boolean of(final Integer type)
            {
                if (super.type.equals(type))
                {
                    System.out.format("Case %d, break = %s\n", type, super.breakOnCompletion);
                    return super.breakOnCompletion;
                }
                else
                {
                    return false;
                }
            }
        };
        integerSwitch.register(case1);
        // more instances for each matching pattern, granted this will get verbose with lots of options but is just
        // and example of how to do standard "switch/case" logic with this pattern.
        integerSwitch.evaluate(0);
        integerSwitch.evaluate(1);
        integerSwitch.evaluate(2);


        final Switch<Integer> inRangeCaseSwitch = new Switch<Integer>();
        final Case<Integer> rangeCase = new InRangeCase<Integer>(5, 100)
        {
            @Override
            public boolean of(final Integer type)
            {
                if (super.inRange(type))
                {
                    System.out.format("Case %s is between %s and %s, break = %s\n", type, this.start, this.end, super.breakOnCompletion);
                    return super.breakOnCompletion;
                }
                else
                {
                    return false;
                }
            }
        };
        inRangeCaseSwitch.register(rangeCase);
        // run some examples
        inRangeCaseSwitch.evaluate(0);
        inRangeCaseSwitch.evaluate(10);
        inRangeCaseSwitch.evaluate(200);

        // combining both types of Case implementations
        integerSwitch.register(rangeCase);
        integerSwitch.evaluate(1);
        integerSwitch.evaluate(10);

    }
}

Это просто соломинка, которую я придумал за несколько минут, более сложная реализация может позволить внедрить какой-то вид Command Patternв Caseэкземпляры реализаций, чтобы сделать его более похожим на стиль обратного вызова IoC.

Как только в этом подходе хорошо то, что все операторы Switch / Case касаются побочных эффектов, это инкапсулирует побочные эффекты в классах, чтобы ими можно было управлять и лучше использовать повторно, в конечном итоге это больше похоже на сопоставление с шаблоном на функциональном языке и это не плохо.

Я буду публиковать любые обновления или улучшения этого Gist на Github.


2
Я согласен, операторы case и большие блоки if неприятны, если у вас большое количество переменных. Если вы делаете много операторов case, значит, вы не используете принципы объектно-ориентированного программирования так хорошо, как могли бы.
Blaine Mucklow

11

По этому вопросу это вполне возможно.

Просто объедините все кейсы, содержащие одну и ту же логику, и не откладывайте breakих.

switch (var) {
    case (value1):
    case (value2):
    case (value3):
        //the same logic that applies to value1, value2 and value3
        break;
    case (value4):
        //another logic
        break;
}

Потому что caseбез breakперескочит к другому caseдо breakили return.

РЕДАКТИРОВАТЬ:

Отвечая на комментарий, если у нас действительно 95 значений с той же логикой, но гораздо меньшее количество случаев с другой логикой, мы можем сделать:

switch (var) {
     case (96):
     case (97):
     case (98):
     case (99):
     case (100):
         //your logic, opposite to what you put in default.
         break;
     default: 
         //your logic for 1 to 95. we enter default if nothing above is met. 
         break;
}

Если вам нужен более тонкий контроль, if-elseэто выбор.


2
Вопрос уже предлагает это как решение и спрашивает, есть ли способ указать диапазон без необходимости кодировать каждое значение в диапазоне (OP потребует 96 caseоператоров!). Боюсь, я согласен с принятым ответом.
Bohemian

Спасибо за комментарий. Смотрите, может быть, отредактируйте. Я согласен, что все зависит от сценария, и 5 против 95 может быть не так.
WesternGun

6

В принципе:

if (variable >= 5 && variable <= 100)
{
    doSomething();
}

Если вам действительно нужно использовать переключатель, это потому, что вам нужно делать разные вещи для определенных диапазонов. В этом случае да, у вас будет беспорядочный код, потому что все становится сложным, и только те вещи, которые следуют шаблонам, будут хорошо сжиматься.

Единственная причина для переключения - сэкономить на вводе имени переменной, если вы просто проверяете числовые значения переключения. Вы не собираетесь включать 100 вещей, и все они не будут делать одно и то же. Это больше похоже на кусок «если».


4

// Пример несовместимого кода

switch (i) {
  case 1:
    doFirstThing();
    doSomething();
    break;
  case 2:
    doSomethingDifferent();
    break;
  case 3:  // Noncompliant; duplicates case 1's implementation
    doFirstThing();
    doSomething();
    break;
  default:
    doTheRest();
}

if (a >= 0 && a < 10) {
  doFirstThing();

  doTheThing();
}
else if (a >= 10 && a < 20) {
  doTheOtherThing();
}
else if (a >= 20 && a < 50) {
  doFirstThing();
  doTheThing();  // Noncompliant; duplicates first condition
}
else {
  doTheRest();
}

// Соответствующее решение

switch (i) {
  case 1:
  case 3:
    doFirstThing();
    doSomething();
    break;
  case 2:
    doSomethingDifferent();
    break;
  default:
    doTheRest();
}

if ((a >= 0 && a < 10) || (a >= 20 && a < 50)) {
  doFirstThing();
  doTheThing();
}
else if (a >= 10 && a < 20) {
  doTheOtherThing();
}
else {
  doTheRest();
}

Фактический ответ, который заслуживает одобрения. Ницца.
user1735921

3

Из последнего выпуска java-12 в функции предварительного просмотра доступно несколько констант в одной метке case.

Он доступен в версии JDK, чтобы вызвать обратную связь от разработчиков, основанную на использовании в реальном мире; это может привести к тому, что он станет постоянным в будущей платформе Java SE.

Это выглядит как:

switch(variable) {
    case 1 -> doSomething();
    case 2, 3, 4 -> doSomethingElse();
};

См. Больше JEP 325: Switch Expressions (предварительная версия)


2

С этим можно справиться с помощью библиотеки Vavr

import static io.vavr.API.*;
import static io.vavr.Predicates.*;

Match(variable).of(
    Case($(isIn(5, 6, ... , 100)), () -> doSomething()),
    Case($(), () -> handleCatchAllCase())
);

Это, конечно, лишь небольшое улучшение, так как все случаи по-прежнему нужно перечислять явно. Но определить собственный предикат легко:

public static <T extends Comparable<T>> Predicate<T> isInRange(T lower, T upper) {
    return x -> x.compareTo(lower) >= 0 && x.compareTo(upper) <= 0;
}

Match(variable).of(
    Case($(isInRange(5, 100)), () -> doSomething()),
    Case($(), () -> handleCatchAllCase())
);

Match - это выражение, поэтому здесь оно возвращает что-то вроде Runnableinstance вместо прямого вызова методов. После того, как матч проведен, Runnableможет быть выполнен.

Для получения дополнительных сведений см. Официальную документацию .


1

в качестве альтернативы вы можете использовать, как показано ниже:

if (variable >= 5 && variable <= 100) {
        doSomething();

    }

или следующий код также работает

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}

1

JEP 354: Switch Expressions (Preview) в JDK-13 и JEP 361: Switch Expressions (Standard) в JDK-14 расширяет оператор switch, чтобы его можно было использовать как выражение .

Теперь вы можете:

  • напрямую назначить переменную из выражения переключателя ,
  • использовать новую форму метки переключателя ( case L ->):

    Код справа от метки переключателя «case L ->» может быть выражением, блоком или (для удобства) оператором throw.

  • используйте несколько констант для каждого случая, разделенных запятыми,
  • а также больше нет разрывов значений :

    Чтобы получить значение из выражения переключения, breakоператор with value отбрасывается в пользу yieldоператора.

Пример выражения переключения:

public class SwitchExpression {

  public static void main(String[] args) {
      int month = 9;
      int year = 2018;
      int numDays = switch (month) {
        case 1, 3, 5, 7, 8, 10, 12 -> 31;
        case 4, 6, 9, 11 -> 30;
        case 2 -> {
          if (java.time.Year.of(year).isLeap()) {
            System.out.println("Wow! It's leap year!");
            yield 29;
          } else {
            yield 28;
          }
        }
        default -> {
          System.out.println("Invalid month.");
          yield 0;
        }
      };
      System.out.println("Number of Days = " + numDays);
  }
}

0

Альтернативой вместо использования жестко заданных значений может быть использование сопоставлений диапазонов в операторе switch:

private static final int RANGE_5_100 = 1;
private static final int RANGE_101_1000 = 2;
private static final int RANGE_1001_10000 = 3;

public boolean handleRanges(int n) {
    int rangeCode = getRangeCode(n);
    switch (rangeCode) {
        case RANGE_5_100: // doSomething();
        case RANGE_101_1000: // doSomething();
        case RANGE_1001_10000: // doSomething();
        default: // invalid range
    }
}

private int getRangeCode(int n) {
    if (n >= 5 && n <= 100) {
        return RANGE_5_100;
    } else if (n >= 101 && n <= 1000) {
        return RANGE_101_1000;
    } else if (n >= 1001 && n <= 10000) {
        return RANGE_1001_10000;
    }

    return -1;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.