Java中的switch语句多个case情况

155

我正在尝试弄清楚如何在Java switch语句中使用多个case。以下是我尝试的示例:

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

相对于不得不做:

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

你认为这是否有可能实现,或者有什么好的替代方案吗?


12
看起来你正在使用整数,所以我猜如果你知道你的范围是固定大小的,你可以始终使用 switch(variable / FIXED_SIZE_OF_RANGE) 的方式进行分支判断。其中 case 0: ... 表示第一个范围,default: break; 则表示其他范围。 - paulrehkugler
16个回答

108
第二个选项完全没问题。我不确定为什么有人说不行。这个方法是可行的,而且我一直都这样做:
switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}

69
问者说要做这个“与”做这个。他知道你列出的是有效的,他想要做第一件事情而不是那个。 - Blaine Mucklow
64
抱歉,但我不明白按顺序列出95个指向同一件事情的案例如何解决任何问题。如果在任何代码中遇到这种情况,我会追踪它们,绑架它们,并亲自将它们送到GLaDOS那里,希望她给它们最致命的测试序列。 - animuson
2
@animuson 顶着60个赞,真是让人难以置信。我来这里是因为我不想做他回答的完全相同的事情。 - killjoy
1
因为这并没有回答这个问题,所以我要点个踩...看起来,回答这个问题的人甚至都没有仔细阅读。 - iheanyi
2
这真是个噩梦,人们怎么会投那么多赞? - Elley
@Elley 欢迎来到 StackOverflow。似乎没有人会认真阅读问题(或者答案),他们只是为了获取积分而急于回答。 - Jack

90

很遗憾,在Java中这是不可能的。您必须使用if-else语句。


1
据我所知,您可以在vb.net中实现这一点。这里是一个示例。 - Bala R
3
我的回答证明这是不正确的,它是OO且是一种已知且被接受的模式,用于处理需要许多嵌套的 if/elseif/else 语句的问题,无论使用何种编程语言。 - user177800
1
可以通过使用以下链接在SWITCH case中获得OR条件...请查看:https://dev59.com/F2Qn5IYBdhLWcg3wmILY#16706729 - Ravindra Kushwaha
4
兄弟,你可以在不使用break的情况下编写多个case语句,并在最后一个case中编写你的逻辑代码。例如:case 某个值: case 另一个值: case 第三个值: 你的逻辑代码写在这里 break; - anoop ghildiyal
1
你一定是在开玩笑,这是完全有可能做到的。只需不在结尾处应用break语句即可。你的回答是误导性的。 - user1735921
显示剩余10条评论

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

输出:

Default case
First case
First case
First case
First case
Default case
Default case
Default case
Second case
Second case

来源: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html

Switch语句是一种用于测试多个条件的控制结构。它可以替代使用一系列if-else if语句的情况。

Switch语句包含一个表达式和一组case标签。每个case标签都包含一个常量值。如果表达式的值与某个case标签的常量值相等,则执行该case标签后的语句。如果没有匹配的case标签,则执行default标签后的语句(如果存在default标签)。

注意:在Java SE 7及更高版本中,switch语句支持字符串类型的表达式。


5
这与他想要避免的“对抗”部分相同。 - Bdoserror
3
仅仅给出代码的答案和仅仅给出链接的答案一样糟糕。它们实际上并不是真正的答案。一个好的答案应该包含解释、推理以及可能的参考来源或权威性证明。 - markus
3
这虽不是最好的答案,但比没有强。有些人将其视为最佳答案,简单明了。 - D4rWiNS

51

也许没有之前的一些答案那么优雅,但如果你想要在几个大范围内实现switch case,请先将范围组合成单个case:

// 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.
} 

13
我不建议这样做。如果你只是简单地运行代码,你会觉得case 1意味着variable == 1,这会导致混乱和长期的痛苦。如果你需要在代码中加注释以使其易读,那么我认为你做错了什么。 - kraxor

23

一种面向对象替代过于庞大的 switchif/else 构造的方法是使用 Chain of Responsibility Pattern 模式来模拟决策过程。

Chain of Responsibility Pattern

责任链模式允许将请求的来源与确定应该执行请求的潜在大量处理程序分开。代表链角色的类将请求从源头沿着处理程序列表传递,直到某个处理程序接受请求并执行它。

以下是一个示例实现,也使用了泛型保证类型安全。

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

    }
}

这只是我在几分钟内草拟的一个简单的想法,如果进行更复杂的实现,可以允许注入一些命令模式(Command Pattern)Case实例中,使它更像回调控制反转(IoC)风格。

这种方法的好处之一是Switch/Case语句都涉及到副作用(side effects),这样将副作用封装在类中,就可以更好地管理和重复使用。最终结果更像是函数式语言中的Pattern Matching,而这并不是一件坏事。

如果有任何更新或增强,我将在Github上发布此Gist


2
我同意,如果你有大量的变量,case语句和大量if块就会变得很麻烦。如果你正在使用很多case语句,那么你可能没有充分利用面向对象编程原则。 - Blaine Mucklow

19

Java 14的switch增强功能使这成为可能。以下是一个相当直观的示例,展示如何实现相同的功能。

switch (month) {
    case 1, 3, 5, 7, 8, 10, 12 -> System.out.println("this month has 31 days");
    case 4, 6, 9 -> System.out.println("this month has 30 days");
    case 2 -> System.out.println("February can have 28 or 29 days");
    default -> System.out.println("invalid month");
}

16
根据这个问题,完全有可能实现。只需将包含相同逻辑的所有情况放在一起,并且不要在它们后面加上break
switch (var) {
    case (value1):
    case (value2):
    case (value3):
        //the same logic that applies to value1, value2 and value3
        break;
    case (value4):
        //another logic
        break;
}

这是因为case没有break语句会一直跳转到下一个case,直到遇到breakreturn语句。

编辑:

回复评论,如果我们有95个具有相同逻辑的值,但只有很少数量的不同逻辑的情况,我们可以这样做:

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;
}
如果您需要更精细的控制,if-else是一个不错的选择。

3
问题已经提供了一个解决方案,并询问是否有一种方法可以指定范围而不必为范围内的每个值编写代码(原帖需要96个“case”语句!)。恐怕我同意被接受的答案。 - Bohemian
感谢评论。看看修改吧,也许可以解决问题。我同意一切取决于情境,5比95可能并不是这种情况。 - WesternGun

9

JEP 354: Switch Expressions (Preview)在JDK-13中,JEP 361: Switch Expressions (Standard)在JDK-14中将扩展switch语句,使其可用作表达式

现在您可以:

  • 直接从switch表达式中分配变量,
  • 使用新的switch标签形式(case L ->):

    "case L ->" switch标签右侧的代码被限制为表达式、块或(为方便起见)抛出语句。

  • 在每个case中使用多个常量,用逗号分隔,
  • 并且不再有值breaks

    为了从switch表达式中产生一个值,放弃了带有值的break语句,取而代之的是一个yield语句。

Switch表达式示例:

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


6
基本上:
if (variable >= 5 && variable <= 100)
{
    doSomething();
}

如果您真的需要使用开关,那是因为您需要根据某些范围执行各种操作。在这种情况下,是的,您的代码会变得混乱,因为事情变得复杂,只有遵循模式的事物才能压缩得好。
开关的唯一理由是为了节省输入变量名称的时间,如果您只是测试数字开关值。您不会切换100个东西,并且它们不会全部执行相同的操作。这听起来更像一个“if”块。

6

在最新的Java 12版本中,同一case标签中可以使用多个常量预览语言特性

此功能在JDK特性版本中提供,旨在通过实际应用促使开发者反馈;这可能导致它在未来的Java SE平台中成为永久特性。

示例如下:

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

更多信息请参见JEP 325: Switch表达式(预览版)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接