Java-Switch-Anweisung mehrere Fälle

118

Ich versuche nur herauszufinden, wie viele Mehrfachfälle für eine Java-switch-Anweisung verwendet werden können. Hier ist ein Beispiel dafür, was ich versuche:

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

versus tun zu müssen:

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

Irgendwelche Ideen, wenn dies möglich ist, oder was ist eine gute Alternative?

FunJavaCode
quelle
12
Es sieht so aus, als würden Sie Ganzzahlen verwenden. Wenn Sie also wissen, dass Ihre Bereiche eine feste Größe haben, können Sie jederzeit umschalten (Variable / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
Paulrehkugler

Antworten:

80

Leider ist dies in Java nicht möglich. Sie müssen auf die Verwendung von if-elseAnweisungen zurückgreifen .

Bala R.
quelle
1
@FunJavaCode AFAIK das kannst du in vb.net machen. Hier ist ein Beispiel
Bala R
1
@YuryLitvinov möchten Sie erweitern, warum Sie denken, dass es falsch ist?
Bala R
1
Meine Antwort beweist, dass dies falsch ist, es ist OO und ein bekanntes und akzeptiertes Muster für die Behandlung dieses genauen Problems unter anderen, die if/elseif/elseunabhängig von der Sprache viele verschachtelte Anweisungen erfordern .
1
Es ist möglich, die ODER-Bedingung im SWITCH-Fall über diesen Link zu erhalten ... Bitte überprüfen Sie es: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha
4
Bro seine mögliche Verwendung kann mehrere Fälle schreiben, ohne break zu verwenden, und am Ende des Falls können Sie Ihre Logik wie folgt schreiben: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2
85

Die zweite Option ist völlig in Ordnung. Ich bin mir nicht sicher, warum ein Antwortender sagte, es sei nicht möglich. Das ist in Ordnung, und ich mache das die ganze Zeit:

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}
Dave
quelle
50
Der Fragesteller sagte, mach das "versus". Er versteht, dass das, was Sie aufgelistet haben, gültig ist, er hat versucht, das erste zu tun, STATT davon.
Blaine Mucklow
45
Es tut mir leid, aber ich sehe nicht, wie die Auflistung von 95 Fällen hintereinander, die sich auf dasselbe beziehen, eine Lösung für irgendetwas ist. Wenn ich in einem Code darauf stoßen würde, würde ich sie aufspüren, entführen, persönlich an GLaDOS liefern und hoffen, dass sie ihnen die tödlichste Folge von Tests gibt, die sie finden kann.
Animuson
1
@animuson, auf dem er 60 Mal upvoted wird .... go figure. Ich bin hierher gekommen, weil ich nicht genau das tun wollte, was er antwortet
killjoy
Downvoting, da dies die Frage nicht beantwortet. . Anscheinend wurde die Frage nicht einmal gelesen, um diese Antwort zu schreiben.
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;
            }
        }
    }
}

Aus:

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
quelle
4
Das ist das gleiche wie der "versus" Teil seiner Frage, den er vermeiden wollte.
Bdoserror
2
Nur-Code-Antworten sind fast so schlecht wie Nur-Link-Antworten. Sie sind überhaupt keine wirklichen Antworten. Eine gute Antwort enthält Erklärungen und Argumente sowie möglicherweise einige Quellen oder Befugnisse.
Markus
3
Es ist besser als nichts, manche Leute nehmen das als die beste Antwort, einfach und direkt
D4rWiNS
48

Vielleicht nicht so elegant wie einige frühere Antworten, aber wenn Sie Schaltfälle mit wenigen großen Bereichen erzielen möchten, kombinieren Sie die Bereiche vorher einfach zu einem einzigen Fall:

// 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
quelle
11
Ich würde das nicht empfehlen. Wenn Sie nur den Code durchgehen, haben Sie die Intuition, die case 1bedeutet variable == 1, was auf lange Sicht zu Verwirrung und viel Schmerz führt. Wenn Sie Kommentare in Ihren Code einfügen müssen, um ihn lesbar zu machen, haben Sie meiner Meinung nach etwas falsch gemacht.
Kraxor
21

Eine objektorientierte Option zum Ersetzen von zu großen switchund if/elseKonstrukten besteht darin, a Chain of Responsibility Patternzur Modellierung der Entscheidungsfindung zu verwenden.

Muster der Verantwortungskette

Das Muster der Verantwortungskette ermöglicht die Trennung der Quelle einer Anforderung von der Entscheidung, welcher der potenziell großen Handler für die Anforderung darauf reagieren soll. Die Klasse, die die Kettenrolle darstellt, kanalisiert die Anforderungen von der Quelle entlang der Liste der Handler, bis ein Handler die Anforderung akzeptiert und sie ausführt.

Hier ist eine Beispielimplementierung, die auch mithilfe von Generika typsicher ist.

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

    }
}

Dies ist nur ein kurzer Strohmann, den ich in wenigen Minuten zusammengestellt habe. Eine komplexere Implementierung könnte es ermöglichen, eine Art Command Patternin die CaseImplementierungsinstanzen zu injizieren , um sie eher zu einem Rückruf-IoC-Stil zu machen.

Sobald das Schöne an diesem Ansatz ist, dass es bei Switch / Case-Anweisungen nur um Nebenwirkungen geht, werden die Nebenwirkungen in Klassen zusammengefasst, damit sie verwaltet und besser wiederverwendet werden können. Am Ende ähnelt dies eher dem Pattern Matching in einer funktionalen Sprache und das ist keine schlechte Sache

Ich werde alle Updates oder Verbesserungen zu diesem Gist auf Github veröffentlichen.


quelle
2
Ich bin damit einverstanden, case-Anweisungen und big if-Blöcke sind böse, wenn Sie eine große Anzahl von Variablen haben. Wenn Sie viele Fallaussagen machen, verwenden Sie die OO-Prinzipien nicht so gut, wie Sie es könnten.
Blaine Mucklow
11

Nach dieser Frage ist es durchaus möglich.

Setzen Sie einfach alle Fälle zusammen, die dieselbe Logik enthalten, und setzen Sie sie nicht breakhinter sich.

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

Es ist, weil caseohne breakzu einem anderen springen wird, casebis breakoder return.

BEARBEITEN:

Wenn wir auf den Kommentar antworten und wirklich 95 Werte mit derselben Logik haben, aber eine viel geringere Anzahl von Fällen mit unterschiedlicher Logik, können wir Folgendes tun:

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

Wenn Sie eine feinere Kontrolle benötigen, if-elseist dies die Wahl.

WesternGun
quelle
2
Die Frage bietet dies bereits als Lösung an und fragt, ob es eine Möglichkeit gibt, einen Bereich anzugeben, ohne jeden Wert im Bereich codieren zu müssen (OP würde 96 caseAnweisungen erfordern !). Ich fürchte, ich stimme der akzeptierten Antwort zu.
Böhmisch
Danke für den Kommentar. Siehe vielleicht bearbeiten. Ich bin damit einverstanden, dass alles vom Szenario abhängt und 5 gegen 95 möglicherweise nicht der Fall ist.
WesternGun
6

Grundsätzlich:

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

Wenn Sie wirklich einen Schalter benötigen, liegt dies daran, dass Sie für bestimmte Bereiche verschiedene Aufgaben ausführen müssen. In diesem Fall, ja, Sie werden unordentlichen Code haben, weil die Dinge komplex werden und nur Dinge, die Mustern folgen, gut komprimiert werden.

Der einzige Grund für einen Wechsel besteht darin, beim Eingeben des Variablennamens zu sparen, wenn Sie nur auf numerische Schaltwerte testen. Sie werden nicht 100 Dinge einschalten, und sie werden nicht alle dasselbe tun. Das klingt eher nach einem Wenn-Stück.

Michael Kozakewich
quelle
4

// Beispiel für nicht konformen Code

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

// Konforme Lösung

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
quelle
Die eigentliche Antwort, die Daumen hoch verdient hat. Nett.
user1735921
1
Vielen Dank user1735921
Manoj Kumar Sharma
3

Ab der letzten Java-12-Version sind mehrere Konstanten in derselben Fallbezeichnung in der Vorschau-Sprachfunktion verfügbar

Es ist in einer JDK-Feature-Version verfügbar, um Entwickler-Feedback basierend auf der realen Verwendung zu provozieren. Dies kann dazu führen, dass es in einer zukünftigen Java SE-Plattform dauerhaft wird.

Es sieht aus wie:

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

Weitere Informationen JEP 325: Ausdrücke wechseln (Vorschau)

Ruslan
quelle
2

Dies ist mit der Vavr- Bibliothek möglich

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

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

Dies ist natürlich nur eine geringfügige Verbesserung, da alle Fälle noch explizit aufgeführt werden müssen. Es ist jedoch einfach, ein benutzerdefiniertes Prädikat zu definieren:

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 ist ein Ausdruck, daher gibt er hier so etwas wie eine RunnableInstanz zurück, anstatt Methoden direkt aufzurufen. Nach dem Match Runnablekann ausgeführt werden.

Weitere Details finden Sie in der offiziellen Dokumentation .

hgrey
quelle
1

Als Alternative können Sie Folgendes verwenden:

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

    }

oder der folgende Code funktioniert auch

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

JEP 354: Switch-Ausdrücke (Vorschau) in JDK-13 und JEP 361: Switch-Ausdrücke (Standard) in JDK-14 erweitern die switch-Anweisung, sodass sie als Ausdruck verwendet werden kann .

Jetzt kannst du:

  • Variable direkt aus Schalterausdruck zuweisen ,
  • Verwenden Sie eine neue Form der Schalterbezeichnung ( case L ->):

    Der Code rechts von der Schalterbezeichnung "case L ->" ist auf einen Ausdruck, einen Block oder (der Einfachheit halber) eine throw-Anweisung beschränkt.

  • Verwenden Sie mehrere durch Kommas getrennte Konstanten pro Fall.
  • und es gibt auch keine wertebrüche mehr :

    Um einen Wert aus einem switch-Ausdruck zu erhalten, wird die breakwith-value-Anweisung zugunsten einer yieldAnweisung gelöscht .

Beispiel für einen Schalterausdruck:

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
quelle
0

Eine Alternative anstelle der Verwendung fest codierter Werte könnte die Verwendung von Bereichszuordnungen in der switch-Anweisung sein:

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
quelle