Instruction de commutation Java plusieurs cas

118

J'essaie simplement de comprendre comment utiliser plusieurs cas multiples pour une instruction de commutation Java. Voici un exemple de ce que j'essaie de faire:

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

plutôt que d'avoir à faire:

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

Des idées si cela est possible, ou quelle est une bonne alternative?

FunJavaCode
la source
12
Il semble que vous utilisiez des entiers donc je suppose que si vous savez que vos plages sont d'une taille fixe, vous pouvez toujours faire switch (variable / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Réponses:

80

Malheureusement, ce n'est pas possible en Java. Vous devrez recourir à des if-elsedéclarations.

Bala R
la source
1
@FunJavaCode AFAIK vous pouvez le faire dans vb.net. Voici un exemple
Bala R
1
@YuryLitvinov voulez-vous développer pourquoi vous pensez que c'est incorrect?
Bala R
1
Ma réponse prouve que c'est incorrect, c'est OO et c'est un modèle connu et accepté pour traiter ce problème exact parmi d'autres qui nécessitent de nombreuses if/elseif/elseinstructions imbriquées , quelle que soit la langue.
1
Il est possible d'obtenir la condition OR dans le cas SWITCH en utilisant ce lien ... veuillez le vérifier: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha
4
bro son utilisation possible peut écrire plusieurs cas sans utiliser break et à la fin de cas, vous pouvez écrire votre logique comme: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2
85

La deuxième option est tout à fait correcte. Je ne sais pas pourquoi un répondant a dit que ce n'était pas possible. C'est bien, et je fais ça tout le temps:

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}
Dave
la source
50
L'intervenant a dit de faire ceci "plutôt que" de faire cela. Il comprend que ce que vous avez énuméré est valide, il essayait de faire la première chose au lieu de cela.
Blaine Mucklow
45
Je suis désolé, mais je ne vois pas en quoi la liste de 95 cas d'affilée qui vont à la même chose est une solution à quoi que ce soit. Si je rencontrais cela dans n'importe quel code, je les traquerais, les kidnapperais, les livrerais personnellement à GLaDOS et j'espère qu'elle leur donnera la séquence de tests la plus meurtrière qu'elle puisse trouver.
animuson
1
@animuson en plus de quoi, il obtient un vote positif 60 fois .... allez comprendre. Je suis venu ici parce que je ne voulais pas faire la chose exacte à laquelle il répond
killjoy
Le vote négatif car cela ne répond pas à la question. . Vérifiez, apparemment, la question n'a même pas été lue pour écrire cette réponse.
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;
            }
        }
    }
}

En dehors:

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
la source
4
C'est la même chose que la partie «contre» de sa question, qu'il voulait éviter.
Bdoserror
2
Les réponses de code uniquement sont presque aussi mauvaises que les réponses de lien uniquement. Ce ne sont pas vraiment des réponses du tout. Une bonne réponse contient des explications et des raisonnements et éventuellement des sources ou une autorité.
markus
3
C'est mieux que rien, certaines personnes considèrent cela comme la meilleure réponse, simple et directe
D4rWiNS
48

Peut-être pas aussi élégant que certaines réponses précédentes, mais si vous souhaitez obtenir des boîtiers de commutation avec quelques grandes plages, il suffit de combiner les plages dans un seul cas au préalable:

// 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
la source
11
Je ne recommanderais pas ça. Si vous parcourez simplement le code, vous aurez l'intuition que cela case 1signifie variable == 1, ce qui conduit à la confusion et à beaucoup de douleur à long terme. Si vous devez placer des commentaires dans votre code pour le rendre lisible, vous avez fait quelque chose de mal à mon humble avis.
kraxor le
21

Une option orientée objet pour remplacer des constructions switchet des if/elseconstructions trop volumineuses consiste à utiliser unChain of Responsibility Pattern pour modéliser la prise de décision.

Modèle de chaîne de responsabilité

Le modèle de chaîne de responsabilité permet de séparer la source d'une demande de décider lequel parmi le nombre potentiellement élevé de gestionnaires de la demande doit la traiter. La classe représentant le rôle de chaîne canalise les demandes de la source le long de la liste des gestionnaires jusqu'à ce qu'un gestionnaire accepte la demande et la traite.

Voici un exemple d'implémentation qui est également Type Safe utilisant 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);

    }
}

C'est juste un homme de paille rapide que j'ai fouetté en quelques minutes, une implémentation plus sophistiquée pourrait permettre d' Command Patterninjecter une sorte deCase instances instances d'implémentations pour en faire un style de rappel IoC.

Une fois que cette approche est intéressante, les instructions Switch / Case concernent uniquement les effets secondaires, cela encapsule les effets secondaires dans les classes afin qu'ils puissent être gérés et mieux réutilisés, cela finit par ressembler davantage à la correspondance de motifs dans un langage fonctionnel et ce n'est pas une mauvaise chose.

Je publierai toutes les mises à jour ou améliorations de ce Gist sur Github.


la source
2
Je suis d'accord, les déclarations de cas et les gros blocs si sont méchants si vous avez une grande quantité de variables. Si vous faites beaucoup d'énoncés de cas, vous n'utilisez pas les principes OO aussi bien que vous pourriez l'être.
Blaine Mucklow
11

Selon cette question , c'est tout à fait possible.

Mettez simplement tous les cas qui contiennent la même logique ensemble, et ne les mettez pas breakderrière.

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

C'est parce que casesans breaksautera à un autre casejusqu'à ce que breakoureturn .

ÉDITER:

En réponse au commentaire, si nous avons vraiment 95 valeurs avec la même logique, mais un nombre beaucoup plus petit de cas avec une logique différente, nous pouvons faire:

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

Si vous avez besoin d'un contrôle plus fin, if-elsec'est le choix.

WesternGun
la source
2
La question propose déjà cela comme solution et demande s'il existe un moyen de spécifier une plage sans avoir à coder chaque valeur de la plage (OP nécessiterait 96 caseinstructions!). J'ai bien peur d'être d'accord avec la réponse acceptée.
Bohème
Merci pour le commentaire. Voir modifier, peut-être. Je conviens que tout dépend du scénario, et 5 contre 95 peut ne pas être le cas.
WesternGun
6

Fondamentalement:

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

Si vous aviez vraiment besoin d'utiliser un commutateur, ce serait parce que vous devez faire différentes choses pour certaines plages. Dans ce cas, oui, vous allez avoir du code désordonné, car les choses deviennent complexes et seules les choses qui suivent des modèles vont bien se compresser.

La seule raison d'un changement est d'économiser sur la saisie du nom de la variable si vous ne testez que des valeurs de commutation numériques. Vous n'allez pas activer 100 choses et ils ne feront pas tous la même chose. Cela ressemble plus à un morceau «si».

Michael Kozakewich
la source
4

// Exemple de code non conforme

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

// Solution conforme

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
la source
La réponse réelle qui méritait un coup de pouce. Agréable.
user1735921
3

Depuis la dernière version de java-12, plusieurs constantes dans la même étiquette de cas sont disponibles dans la fonction de langue de prévisualisation

Il est disponible dans une version de fonctionnalité JDK pour provoquer des commentaires des développeurs basés sur une utilisation réelle; cela pourrait le conduire à devenir permanent dans une future plate-forme Java SE.

On dirait:

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

Voir plus JEP 325: Switch Expressions (aperçu)

Ruslan
la source
2

Il est possible de gérer cela en utilisant la bibliothèque Vavr

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

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

Il ne s'agit bien sûr que d'une légère amélioration puisque tous les cas doivent encore être répertoriés explicitement. Mais il est facile de définir un prédicat personnalisé:

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 est une expression donc ici, elle renvoie quelque chose comme Runnableinstance au lieu d'appeler directement des méthodes. Après le matchRunnable peut être exécuté.

Pour plus de détails, veuillez consulter la documentation officielle .

hgrey
la source
1

pour l'alternative, vous pouvez utiliser comme ci-dessous:

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

    }

ou le code suivant fonctionne également

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

JEP 354: Switch Expressions (Preview) dans JDK-13 et JEP 361: Switch Expressions (Standard) in JDK-14 étendra l' instruction switch afin qu'elle puisse être utilisée comme expression .

Maintenant vous pouvez:

  • attribuer directement une variable à partir de l' expression de commutateur ,
  • utiliser une nouvelle forme d'étiquette de commutateur (case L -> ):

    Le code à droite d'une étiquette de commutateur "case L ->" est limité à une expression, un bloc ou (pour des raisons pratiques) une instruction throw.

  • utiliser plusieurs constantes par cas, séparées par des virgules,
  • et aussi il n'y a plus de coupures de valeur :

    Pour générer une valeur à partir d'une expression de commutateur, l' breakinstruction with value est supprimée en faveur d'une yieldinstruction.

Exemple d'expression de commutateur:

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);
  }
}
Alexandre Iskuskov
la source
0

Une alternative au lieu d'utiliser des valeurs codées en dur pourrait être d'utiliser des mappages de plage sur l'instruction switch à la place:

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
la source