Vários casos de instrução switch Java

118

Apenas tentando descobrir como usar vários casos múltiplos para uma instrução switch Java. Aqui está um exemplo do que estou tentando fazer:

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

versus ter que fazer:

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

Alguma ideia, se possível, ou qual é uma boa alternativa?

FunJavaCode
fonte
12
Parece que você está usando números inteiros, então suponho que se você souber que seus intervalos são de um tamanho fixo, você sempre pode mudar (variável / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Respostas:

80

Infelizmente, não é possível em Java. Você terá que recorrer ao uso de if-elsedeclarações.

Bala R
fonte
1
@FunJavaCode AFAIK você pode fazer isso no vb.net. Aqui está um exemplo
Bala R
1
@YuryLitvinov você deseja expandir porque você acha que está incorreto?
Bala R de
1
Minha resposta prova que isso está incorreto, é OO e é um padrão conhecido e aceito para lidar com esse problema exato entre outros que requerem muitas if/elseif/elseinstruções aninhadas , independentemente do idioma.
1
É possível obter a condição OR no caso SWITCH usando este link ... por favor, verifique: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha
4
bro seu uso possível pode escrever case múltiplo sem usar break e no final do case você pode escrever sua lógica como: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2
85

A segunda opção está perfeitamente bem. Não sei por que um respondente disse que não era possível. Isso é bom, e eu faço isso o tempo todo:

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}
Dave
fonte
50
O questionador disse fazer isso "versus" fazer isso. Ele entende que o que você listou é válido, ele estava tentando fazer a primeira coisa EM VEZ disso.
Blaine Mucklow
45
Sinto muito, mas não vejo como listar 95 casos consecutivos que vão para a mesma coisa seja a solução para qualquer coisa. Se eu encontrasse isso em qualquer código, eu iria rastreá-los, sequestrá-los, entregá-los pessoalmente ao GLaDOS e esperar que ela lhes dê a sequência de testes mais mortal que ela puder encontrar.
animuson
1
@animuson além disso, ele recebe 60 votos a favor ... vai entender. Eu vim aqui porque não queria fazer a coisa EXATA que ele responde
estraga
Votar negativamente, pois isso não responde à pergunta. . .verifique, aparentemente, a pergunta nem foi lida para escrever esta resposta.
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;
            }
        }
    }
}

Fora:

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
fonte
4
Isso é o mesmo que a parte "versus" de sua pergunta, que ele queria evitar.
Bdoserror
2
Respostas somente com código são quase tão ruins quanto respostas somente com link. Eles não são realmente respostas de forma alguma. Uma boa resposta contém explicações e raciocínios e, possivelmente, algumas fontes ou autoridade.
markus
3
É melhor do que nada, algumas pessoas tomam isso como a melhor resposta, simples e direta
D4rWiNS
48

Talvez não seja tão elegante quanto algumas respostas anteriores, mas se você deseja obter casos de switch com poucos intervalos grandes, basta combinar os intervalos para um único caso de antemão:

// 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
fonte
11
Eu não recomendaria isso. Se você apenas executar o código, terá a intuição que isso case 1significa variable == 1, o que leva a confusão e muita dor a longo prazo. Se você precisa colocar comentários em seu código para torná-lo legível, então você fez algo errado IMHO.
kraxor
21

Uma opção orientada a objeto para substituir construções switche if/elseconstruções excessivamente grandes é usar um Chain of Responsibility Patternpara modelar a tomada de decisão.

Padrão de Cadeia de Responsabilidade

O padrão da cadeia de responsabilidade permite a separação da origem de uma solicitação de decidir qual dos potencialmente grande número de manipuladores para a solicitação deve acioná-la. A classe que representa a função da cadeia canaliza as solicitações da fonte ao longo da lista de manipuladores até que um manipulador aceite a solicitação e as acione.

Aqui está um exemplo de implementação que também é de tipo seguro 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 é apenas um espantalho rápido que preparei em alguns minutos, uma implementação mais sofisticada pode permitir que algum tipo de Command Patterninjeção noCase instâncias de implementação para torná-lo mais um estilo de retorno de chamada IoC.

Uma coisa boa sobre essa abordagem é que as instruções Switch / Case são todas sobre efeitos colaterais, isso encapsula os efeitos colaterais em classes para que possam ser gerenciados e reutilizados melhor, acaba sendo mais parecido com a correspondência de padrões em uma linguagem funcional e isso não é uma coisa ruim.

Vou postar quaisquer atualizações ou melhorias para este Gist no Github.


fonte
2
Eu concordo, declarações de caso e blocos grandes se são desagradáveis ​​se você tem uma grande quantidade de variáveis. Se você estiver fazendo muitas declarações de caso, então não está usando os princípios OO tão bem quanto poderia.
Blaine Mucklow
11

De acordo com essa pergunta , é totalmente possível.

Basta colocar todos os casos que contêm a mesma lógica juntos e não colocá- breaklos atrás deles.

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

É porque casesem breakvai pular para outro caseaté breakoureturn .

EDITAR:

Respondendo ao comentário, se realmente temos 95 valores com a mesma lógica, mas um número bem menor de casos com lógica diferente, podemos fazer:

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

Se você precisa de um controle mais preciso, if-elseé a escolha.

WesternGun
fonte
2
A questão já oferece isso como uma solução e pergunta se existe uma maneira de especificar um intervalo sem ter que codificar todos os valores no intervalo (OP exigiria 96 caseinstruções!). Receio concordar com a resposta aceita.
Boêmio
Obrigado por comentar. Veja editar, talvez. Concordo que tudo depende do cenário, e 5 contra 95 pode não ser o caso.
WesternGun
6

Basicamente:

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

Se você realmente precisasse usar um switch, seria porque precisa fazer várias coisas para certos intervalos. Nesse caso, sim, você terá um código confuso, porque as coisas estão ficando complexas e apenas as coisas que seguem padrões irão compactar bem.

A única razão para uma troca é economizar ao digitar o nome da variável se você estiver apenas testando os valores de comutação numéricos. Você não vai ligar 100 coisas, e elas não vão estar todas fazendo a mesma coisa. Isso soa mais como um pedaço "se".

Michael Kozakewich
fonte
4

// Exemplo de código não compatível

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

// Solução compatível

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
fonte
A resposta real que merecia aprovação. Agradável.
user1735921
3

Desde a última versão do java-12, várias constantes no mesmo rótulo de caixa estão disponíveis no recurso de linguagem de visualização

Ele está disponível em um lançamento de recurso JDK para provocar feedback do desenvolvedor com base no uso do mundo real; isso pode fazer com que ele se torne permanente em uma futura plataforma Java SE.

Parece:

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

Veja mais JEP 325: Switch Expressions (Preview)

Ruslan
fonte
2

É possível lidar com isso usando a biblioteca Vavr

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

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

É claro que isso é apenas uma pequena melhoria, já que todos os casos ainda precisam ser listados explicitamente. Mas é fácil definir o 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 é uma expressão, então aqui ela retorna algo como Runnableinstância em vez de invocar métodos diretamente. Após a partida ser realizadaRunnable pode ser executada.

Para obter mais detalhes, consulte a documentação oficial .

Hgrey
fonte
1

como alternativa, você pode usar como abaixo:

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

    }

ou o código a seguir também funciona

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

JEP 354: Switch Expressions (Preview) em JDK-13 e JEP 361: Switch Expressions (Standard) em JDK-14 irá estender a instrução switch para que possa ser usada como uma expressão .

Agora você pode:

  • atribuir diretamente a variável da expressão switch ,
  • use uma nova forma de etiqueta de switch (case L -> ):

    O código à direita de um rótulo de switch "case L ->" é restrito a ser uma expressão, um bloco ou (por conveniência) uma instrução throw.

  • use várias constantes por caso, separadas por vírgulas,
  • e também não há mais quebras de valor :

    Para produzir um valor de uma expressão switch, a breakinstrução with value é descartada em favor de uma yieldinstrução.

Alternar exemplo de expressão:

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

Uma alternativa em vez de usar valores embutidos em código poderia ser usar mapeamentos de intervalo na instrução 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
fonte