Declaración de cambio de Java múltiples casos

118

Solo estoy tratando de descubrir cómo usar muchos casos múltiples para una declaración de cambio de Java. Aquí hay un ejemplo de lo que estoy tratando de hacer:

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

versus tener que hacer:

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

¿Alguna idea si esto es posible, o cuál es una buena alternativa?

FunJavaCode
fuente
12
Parece que estás usando números enteros, así que supongo que si sabes que tus rangos son de un tamaño fijo, siempre puedes cambiar (variable / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Respuestas:

80

Lamentablemente, no es posible en Java. Tendrás que recurrir al uso de if-elsedeclaraciones.

Bala R
fuente
1
@FunJavaCode AFAIK puedes hacerlo en vb.net. Aquí hay un ejemplo
Bala R
1
@YuryLitvinov ¿quieres expandir por qué crees que es incorrecto?
Bala R
1
Mi respuesta prueba que esto es incorrecto, es OO y es un patrón conocido y aceptado para tratar este problema exacto entre otros que requieren muchas if/elseif/elsedeclaraciones anidadas , independientemente del idioma.
1
Es posible obtener la condición OR en el caso de SWITCH utilizando este enlace ... por favor, compruébelo: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha
4
hermano, su posible uso puede escribir múltiples casos sin usar break y al final del caso puede escribir su lógica como: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2
85

La segunda opción está completamente bien. No estoy seguro de por qué un respondedor dijo que no era posible. Esto está bien, y hago esto todo el tiempo:

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}
Dave
fuente
50
El interlocutor dijo hacer esto "versus" hacer esto. Entiende que lo que mencionaste es válido, estaba tratando de hacer lo primero EN LUGAR de eso.
Blaine Mucklow
45
Lo siento, pero no veo cómo enumerar 95 casos seguidos que van a la misma cosa sea una solución para algo. Si encontraba eso en cualquier código, los rastrearía, los secuestraría, se los entregaría personalmente a GLaDOS y espero que les dé la secuencia de pruebas más letal que pueda encontrar.
animuson
1
@animuson, además de eso, recibe 60 votos a favor ... imagínate. Vine aquí porque no quería hacer lo EXACTO que responde
killjoy
Votar en contra ya que esto no responde a la pregunta. . .heck, aparentemente, la pregunta ni siquiera se leyó para escribir esta respuesta.
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;
            }
        }
    }
}

Fuera:

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
fuente
4
Es lo mismo que la parte "versus" de su pregunta, que quería evitar.
Bdoserror
2
Las respuestas de solo código son casi tan malas como las respuestas de solo enlace. Realmente no son respuestas en absoluto. Una buena respuesta contiene explicaciones y razonamientos y posiblemente algunas fuentes o autoridad.
Markus
3
Es mejor que nada, algunas personas lo toman como la mejor respuesta, simple y directa
D4rWiNS
48

Tal vez no sea tan elegante como algunas respuestas anteriores, pero si desea lograr casos de cambio con pocos rangos grandes, simplemente combine los rangos en un solo caso de antemano:

// 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
fuente
11
No recomendaría esto. Si simplemente ejecuta el código, tendrá la intuición que case 1significa variable == 1, lo que genera confusión y mucho dolor a largo plazo. Si necesita colocar comentarios en su código para que sea legible, entonces hizo algo mal en mi humilde opinión.
Kraxor
21

Un objeto opción orientada a reemplazar excesivamente grande switchy if/elseconstrucciones es utilizar una Chain of Responsibility Patternpara modelar la toma de decisiones.

Patrón de cadena de responsabilidad

El patrón de cadena de responsabilidad permite separar la fuente de una solicitud de decidir cuál de la cantidad potencialmente grande de manejadores de la solicitud debe actuar. La clase que representa el rol de la cadena canaliza las solicitudes de la fuente a lo largo de la lista de controladores hasta que un controlador acepta la solicitud y la procesa.

A continuación, se muestra una implementación de ejemplo que también es Type Safe usando genéricos.

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

    }
}

Este es solo un hombre de paja rápido que preparé en unos minutos, una implementación más sofisticada podría permitir Command Patternque se inyecte algún tipo deCase implementación instancias de implementación para que sea más un estilo de devolución de llamada de IoC.

Una vez que lo bueno de este enfoque es que las declaraciones Switch / Case tienen que ver con los efectos secundarios, esto encapsula los efectos secundarios en las clases para que se puedan administrar y reutilizar mejor, termina siendo más como Pattern Matching en un lenguaje funcional y eso no es malo.

Publicaré cualquier actualización o mejora de este Gist en Github.


fuente
2
Estoy de acuerdo, las declaraciones de casos y los grandes bloques if son desagradables si tienes una gran cantidad de variables. Si está haciendo muchas declaraciones de casos, entonces no está utilizando los principios de OO tan bien como podría.
Blaine Mucklow
11

Según esta pregunta , es totalmente posible.

Simplemente ponga todos los casos que contienen la misma lógica juntos, y no los deje breakatrás.

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 porque casesin breaksaltará a otro casehasta breakoreturn .

EDITAR:

Respondiendo al comentario, si realmente tenemos 95 valores con la misma lógica, pero un número mucho menor de casos con lógica diferente, podemos hacer:

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 necesita un control más fino, if-elsees la elección.

WesternGun
fuente
2
La pregunta ya ofrece esto como una solución y pregunta si hay una manera de especificar un rango sin tener que codificar cada valor en el rango (¡OP requeriría 96 casedeclaraciones!). Me temo que estoy de acuerdo con la respuesta aceptada.
Bohemio
Gracias por comentar. Ver editar, tal vez. Estoy de acuerdo en que todo depende del escenario, y 5 vs 95 puede no ser el caso.
WesternGun
6

Básicamente:

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

Si realmente necesitara usar un interruptor, sería porque necesita hacer varias cosas para ciertos rangos. En ese caso, sí, tendrá un código desordenado, porque las cosas se están volviendo complejas y solo las cosas que siguen patrones se comprimirán bien.

La única razón para un cambio es ahorrar al escribir el nombre de la variable si solo está probando valores de cambio numéricos. No vas a encender 100 cosas y no van a hacer todas lo mismo. Eso suena más como un fragmento de 'si'.

Michael Kozakewich
fuente
4

// Ejemplo de código no 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();
}

// Solución compatible

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
fuente
La respuesta real que merecía un pulgar hacia arriba. Agradable.
user1735921
3

Desde la última versión de java-12, varias constantes en la misma etiqueta de caso están disponibles en la función de idioma de vista previa

Está disponible en una versión de función JDK para provocar comentarios de los desarrolladores basados ​​en el uso en el mundo real; esto puede llevar a que se convierta en permanente en una futura plataforma Java SE.

Parece que:

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

Ver más JEP 325: Cambiar expresiones (vista previa)

Ruslan
fuente
2

Es posible manejar esto usando la biblioteca Vavr

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

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

Por supuesto, esto es solo una pequeña mejora, ya que todos los casos deben enumerarse explícitamente. Pero es fácil definir un predicado personalizado:

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 es una expresión, por lo que aquí devuelve algo como Runnableinstancia en lugar de invocar métodos directamente. Después de que se realiza el partidoRunnable puede ejecutar.

Para más detalles, consulte la documentación oficial .

hgrey
fuente
1

como alternativa, puede utilizar de la siguiente manera:

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

    }

o el siguiente código también funciona

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

JEP 354: Expresiones de cambio (vista previa) en JDK-13 y JEP 361: Expresiones de cambio (estándar) en JDK-14 ampliarán la declaración de cambio para que pueda usarse como una expresión .

Ahora usted puede:

  • asignar directamente la variable de la expresión del interruptor ,
  • use una nueva forma de etiqueta de interruptor ( case L ->):

    El código a la derecha de una etiqueta de interruptor "case L ->" está restringido a ser una expresión, un bloque o (por conveniencia) una declaración throw.

  • usar múltiples constantes por caso, separadas por comas,
  • y también no hay más rupturas de valor :

    Para obtener un valor de una expresión de cambio, la breakdeclaración with value se elimina a favor de una yielddeclaración.

Ejemplo de cambio de expresión:

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

Una alternativa en lugar de usar valores codificados de forma rígida podría ser usar asignaciones de rango en la declaración de cambio:

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