Instrukcja przełączania języka Java w wielu przypadkach

118

Próbuję tylko dowiedzieć się, jak użyć wielu wielokrotnych przypadków dla instrukcji przełącznika Java. Oto przykład tego, co próbuję zrobić:

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

w porównaniu z koniecznością:

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

Jakieś pomysły, jeśli to możliwe, lub jaka jest dobra alternatywa?

FunJavaCode
źródło
12
Wygląda na to, że używasz liczb całkowitych, więc przypuszczam, że jeśli wiesz, że twoje zakresy mają stałą wielkość, zawsze możesz zmienić (zmienna / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Odpowiedzi:

80

Niestety w Javie nie jest to możliwe. Będziesz musiał uciekać się do używania if-elseoświadczeń.

Bala R
źródło
1
@FunJavaCode AFAIK, możesz to zrobić w vb.net. Oto przykład
Bala R
1
@YuryLitvinov chcesz rozszerzyć, dlaczego uważasz, że jest to nieprawidłowe?
Bala R
1
Moja odpowiedź dowodzi, że jest to niepoprawne, jest to OO i jest to znany i akceptowany wzorzec radzenia sobie z tym konkretnym problemem wśród innych, które wymagają wielu zagnieżdżonych if/elseif/elsestwierdzeń, niezależnie od języka.
1
Warunek OR w przypadku SWITCH można uzyskać, korzystając z tego linku ... plz sprawdź to: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha
4
bracie, jego możliwe użycie może napisać wiele wielkości liter bez użycia break, a na końcu przypadku możesz napisać swoją logikę w stylu: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2
85

Druga opcja jest całkowicie w porządku. Nie jestem pewien, dlaczego ankietowany powiedział, że to niemożliwe. To jest w porządku i robię to cały czas:

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}
Dave
źródło
50
Pytający powiedział, że rób to „kontra” robienie tego. Rozumie, że to, co podałeś, jest ważne, próbował zrobić pierwszą rzecz ZAMIAST tego.
Blaine Mucklow
45
Przykro mi, ale nie widzę, w jaki sposób wyszczególnienie 95 przypadków pod rząd, które dotyczą tego samego, jest rozwiązaniem wszystkiego. Gdybym napotkał to w jakimkolwiek kodzie, wytropiłbym ich, porwałbym, osobiście dostarczę do GLaDOS i mam nadzieję, że da im najbardziej śmiercionośną sekwencję testów, jaką może znaleźć.
animuson
1
Na dodatek @animuson został uznany 60 razy ... Przyszedłem tutaj, bo nie chciałem zrobić DOKŁADNEJ rzeczy, na którą odpowiada
killjoy
Głosowanie przeciw, ponieważ to nie odpowiada na pytanie. . .Check najwyraźniej pytanie nie zostało nawet przeczytane, aby napisać tę odpowiedź.
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;
            }
        }
    }
}

Na zewnątrz:

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

P. Waksman
źródło
4
To to samo, co część „przeciw” w jego pytaniu, której chciał uniknąć.
Bdoserror
2
Odpowiedzi zawierające tylko kod są prawie tak złe, jak odpowiedzi zawierające tylko linki. To wcale nie są odpowiedzi. Dobra odpowiedź zawiera wyjaśnienia i uzasadnienie oraz prawdopodobnie źródła lub autorytety.
markus
3
Lepiej niż nic, niektórzy uważają to za najlepszą odpowiedź, prostą i bezpośrednią
D4rWiNS
48

Może nie tak eleganckie, jak niektóre poprzednie odpowiedzi, ale jeśli chcesz uzyskać przełączniki z kilkoma dużymi zakresami, po prostu połącz wcześniej zakresy w jeden przypadek:

// 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.
} 
Santtu Kähkönen
źródło
11
Nie polecałbym tego. Jeśli po prostu przejrzysz kod, będziesz miał intuicję, która case 1to oznacza variable == 1, co na dłuższą metę prowadzi do nieporozumień i wielu problemów. Jeśli potrzebujesz umieścić komentarze w swoim kodzie, aby był czytelny, oznacza to, że zrobiłeś coś złego IMHO.
kraxor
21

Jedną z opcji zorientowanych obiektowo do zastąpienia zbyt dużych switchi if/elsekonstrukcji jest użycieChain of Responsibility Pattern do modelowania procesu podejmowania decyzji.

Wzór łańcucha odpowiedzialności

Wzorzec łańcucha odpowiedzialności pozwala na oddzielenie źródła żądania od decyzji, która z potencjalnie dużej liczby osób obsługujących żądanie powinna je wykonać. Klasa reprezentująca rolę łańcuchową kieruje żądania ze źródła wzdłuż listy programów obsługi, dopóki program obsługi nie zaakceptuje żądania i nie wykona na nim akcji.

Oto przykładowa implementacja, która jest również bezpieczna typu przy użyciu typów ogólnych.

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

    }
}

To tylko szybki człowiek ze słomki, który przygotowałem w kilka minut, bardziej wyrafinowana implementacja może pozwolić na Command Patternwstrzyknięcie jakiegoś rodzajuCase instancji implementacji, aby uczynić go bardziej stylem wywołania zwrotnego IoC.

Kiedyś fajną rzeczą w tym podejściu jest to, że instrukcje Switch / Case dotyczą tylko efektów ubocznych, co zawiera efekty uboczne w klasach, dzięki czemu można nimi zarządzać i lepiej ponownie używać, kończy się bardziej jak dopasowywanie wzorców w języku funkcjonalnym i to nie jest złe.

Będę publikować wszelkie aktualizacje lub ulepszenia tego streszczenia na Github.


źródło
2
Zgadzam się, opisy przypadków i duże bloki if są nieprzyjemne, jeśli masz dużą liczbę zmiennych. Jeśli robisz wiele opisów przypadków, nie korzystasz z zasad OO tak dobrze, jak możesz.
Blaine Mucklow
11

Zgodnie z tym pytaniem jest to całkowicie możliwe.

Po prostu zbierz wszystkie przypadki, które zawierają tę samą logikę, i nie stawiaj breakza nimi.

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

To dlatego, że casebez breakprzeskoczy do innego casedo breaklub return.

EDYTOWAĆ:

Odpowiadając na komentarz, jeśli naprawdę mamy 95 wartości z tą samą logiką, ale o wiele mniejszą liczbę przypadków z inną logiką, możemy zrobić:

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;
}

Jeśli potrzebujesz lepszej kontroli, if-elseto wybór.

WesternGun
źródło
2
Pytanie już oferuje to jako rozwiązanie i pyta, czy istnieje sposób na określenie zakresu bez konieczności kodowania każdej wartości w zakresie (OP wymagałoby 96 caseinstrukcji!). Obawiam się, że zgadzam się z zaakceptowaną odpowiedzią.
Bohemian
Dziękuję za komentarz. Może zobacz edycję. Zgadzam się, że wszystko zależy od scenariusza, a 5 na 95 może nie być.
WesternGun
6

Gruntownie:

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

Jeśli naprawdę potrzebujesz użyć przełącznika, to dlatego, że musisz robić różne rzeczy dla określonych zakresów. W takim przypadku tak, będziesz mieć niechlujny kod, ponieważ sprawy stają się skomplikowane i tylko rzeczy, które podążają za wzorcami, będą dobrze kompresowane.

Jedynym powodem zmiany jest oszczędność na wpisywaniu nazwy zmiennej, jeśli testujesz tylko wartości liczbowe przełączania. Nie włączysz 100 rzeczy i nie wszystkie będą robić to samo. To brzmi bardziej jak fragment „jeśli”.

Michael Kozakewich
źródło
4

// Przykład niezgodnego kodu

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

// Zgodne rozwiązanie

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();
}
Manoj Kumar Sharma
źródło
Właściwa odpowiedź, która zasługiwała na kciuki w górę. Miły.
user1735921
3

Od ostatniego wydania java-12 wiele stałych w tej samej etykiecie przypadku jest dostępnych w wersji zapoznawczej języka

Jest dostępny w wersji JDK, aby prowokować opinie programistów w oparciu o rzeczywiste wykorzystanie; może to doprowadzić do tego, że stanie się on trwały w przyszłej platformie Java SE.

To wygląda jak:

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

Zobacz więcej JEP 325: Przełącz wyrażenia (zapowiedź)

Rusłan
źródło
2

Jest to możliwe przy użyciu biblioteki Vavr

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

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

Jest to oczywiście tylko niewielka poprawa, ponieważ wszystkie przypadki nadal muszą być wyraźnie wymienione. Ale łatwo jest zdefiniować predykat niestandardowy:

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

Dopasowanie jest wyrażeniem, więc tutaj zwraca coś w rodzaju Runnableinstancji zamiast bezpośredniego wywoływania metod. Po wykonaniu meczu Runnablemoże zostać wykonany.

Więcej informacji można znaleźć w oficjalnej dokumentacji .

hgrey
źródło
1

alternatywnie możesz użyć jak poniżej:

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

    }

lub poniższy kod również działa

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}
asok
źródło
1

JEP 354: Switch Expressions (Preview) in JDK-13 i JEP 361: Switch Expressions (Standard) w JDK-14 rozszerzy instrukcję switch, dzięki czemu może być używana jako wyrażenie .

Teraz możesz:

  • bezpośrednio przypisuj zmienną z wyrażenia przełączającego ,
  • użyj nowej formy etykiety przełącznika ( case L ->):

    Kod po prawej stronie etykiety przełącznika „case L ->” jest ograniczony do wyrażenia, bloku lub (dla wygody) instrukcji throw.

  • użyj wielu stałych na każdy przypadek, oddzielonych przecinkami,
  • a także nie ma już przerw wartości :

    Aby uzyskać wartość z wyrażenia przełączającego, instrukcja breakwith value jest odrzucana na rzecz yieldinstrukcji.

Przykład wyrażenia przełącznika:

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);
  }
}
Iskuskov Alexander
źródło
0

Alternatywą zamiast używania wartości zakodowanych na stałe może być użycie mapowania zakresu w instrukcji 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;
}
guilhebl
źródło