Java перемикає заяву кілька випадків


118

Просто намагаюся розібратися, як використовувати безліч випадків для оператора перемикання Java. Ось приклад того, що я намагаюся зробити:

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

проти: робити:

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

Будь-які ідеї, якщо це можливо, чи яка хороша альтернатива?


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

Відповіді:


80

На жаль, це неможливо на Java. Вам доведеться вдатися до використання if-elseоператорів.


1
@FunJavaCode AFAIK це можна зробити на vb.net. Ось приклад
Bala R

1
@YuryLitvinov Ви хочете розширити, чому ви вважаєте, що це неправильно?
Бала R

1
Моя відповідь доводить, що це невірно, це OO і є відомим і прийнятим зразком для вирішення цієї точної проблеми серед інших, які потребують багатьох вкладених if/elseif/elseтверджень незалежно від мови.

1
Можна отримати стан АБО у випадку SWITCH, скориставшись цим посиланням ... plz перевірити це: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha

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

85

Другий варіант - цілком чудово. Я не впевнений, чому відповідь сказав, що це неможливо. Це добре, і я роблю це постійно:

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

50
Запитуючий сказав, що робіть це "проти". Він розуміє, що те, що ви перерахували, є дійсним, він намагався зробити перше, що замість цього.
Блейн Маклоу

45
Вибачте, але я не бачу, як перерахування 95 випадків поспіль, які стосуються однієї і тієї ж речі, є рішенням будь-чого. Якщо я зіткнувся з тим, що в будь-якому коді я би відшукав їх, викрав їх, доставив їх особисто в GLaDOS і сподіваюся, що вона надасть їм смертельну послідовність тестів, які вона зможе знайти.
animuson

1
@animuson, на вершині якого, він отримує перевагу в 60 разів. Я прийшов сюди, бо я не хотів робити
ТОЧНУ

Відхилення, оскільки це не дає відповіді на питання. . .вже, мабуть, питання навіть не було прочитане для того, щоб написати цю відповідь.
iheanyi

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

Src: 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, що призводить до плутанини і багато болю в довгостроковій перспективі. Якщо вам потрібно розмістити коментарі у своєму коді, щоб зробити його читабельним, то ви зробили щось неправильне IMHO.
kraxor

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 стилю.

Як тільки приємно в цьому підході полягає в тому, що твердження про перемикання / випадок стосуються побічних ефектів, це інкапсулює побічні ефекти в Класах, щоб ними можна було керувати та повторно використовувати їх, і в кінцевому підсумку більше нагадує узор відповідності шаблонів у функціональній мові та це не погано.

Я опублікую будь-які оновлення або вдосконалення цього Gistub на Github.


2
Я погоджуюся, твердження про регістри і великі, якщо блоки неприємні, якщо у вас є велика кількість змінних. Якщо ви робите багато тверджень про випадки, то ви не використовуєте принципів ОО так добре, як могли б бути.
Блейн Маклоу

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заяв!). Боюся, я згоден з прийнятою відповіддю.
богем

Дякуємо за коментар Дивіться редагування, можливо. Я погоджуюся, що все залежить від сценарію, і 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();
}

Фактична відповідь, яка заслужила великих пальців. Приємно.
користувач1735921

3

З останнього випуску java-12 декілька констант у тому ж корпусі мітка доступна у функції попереднього перегляду

Він доступний у випуску функції JDK, щоб викликати відгуки розробників на основі використання в реальному світі; це може призвести до того, що вона стане постійною в майбутній платформі Java SE.

Це виглядає як:

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

Дивіться більше JEP 325: Перемикання виразів (попередній перегляд)


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())
);

Збіг - це вираз, тому тут він повертає щось на зразок Runnableекземпляра замість виклику методів безпосередньо. Після проведення матчу Runnableможе бути виконаний.

Детальнішу інформацію можна отримати в офіційній документації .


1

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

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

    }

або наступний код також працює

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

1

JEP 354: Перемикання виразів (попередній перегляд) у JDK-13 та JEP 361: Вираження комутації (стандартні) у JDK-14 розширить оператор перемикання, щоб його можна було використовувати як вираз .

Тепер ти можеш:

  • безпосередньо призначити змінну з виразу перемикача ,
  • використовувати нову форму мітки перемикача ( case L ->):

    Код праворуч від мітки перемикача "case L ->" може бути виразом, блоком або (для зручності) операцією кидка.

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

    Щоб отримати значення з виразу перемикача, breakоператор зі значенням знижується на користь 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

Одним із варіантів замість використання жорстко закодованих значень може бути використання відображення діапазону на операторі перемикача:

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.