Java switch语句多种情况


118

只是想弄清楚如何对Java switch语句使用多种情况。这是我要执行的操作的一个示例:

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

与必须要做的事情:

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

有什么想法,如果可能的话,或者什么是好的选择?


12
看来您使用的是整数,所以我想如果您知道范围是固定大小的,则可以随时进行切换(变量/ FIXED_SIZE_OF_RANGE){情况0:...默认值:break; }
paulrehkugler 2013年

Answers:


80

可悲的是,这在Java中是不可能的。您将不得不使用using if-else语句。


1
@FunJavaCode AFAIK可以在vb.net中完成。这是一个例子
Bala R

1
@YuryLitvinov您想扩展为什么您认为它不正确吗?
Bala R

1
我的答案证明这是不正确的,它是面向对象的,并且是一种在其他需要许多嵌套if/elseif/else语句的人中处理这一确切问题的已知且可接受的模式,而与语言无关。

1
通过使用以下链接,可以在SWITCH情况下获得OR条件...请检查以下内容:-stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha,2016年

4
兄弟可能的使用情况可以写多个情况而无需使用break,并且在case结束时可以编写逻辑,例如:case some_value:case some_value:case some_value:you_logic_goes_here break;
anoopbryan2'9

85

第二种选择是完全可以的。我不确定为什么响应者说这是不可能的。很好,我一直这样做:

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

50
发问者说做这个“对”做。他了解您列出的内容是有效的,因此他试图做INSTEAD的第一件事。
布莱恩·穆克洛

45
抱歉,但我看不到连续列出95个涉及同一问题的案例如何解决所有问题。如果我在任何代码中都遇到了这种情况,我将对其进行跟踪,绑架,并将其亲自提供给GLaDOS,并希望她给他们提供最致命的测试序列。
animuson

1
@animuson最重要的是,他获得了60次投票。我来这里是因为我不想做他回答的确切事情
killjoy

否决票不能解决问题。。.heck,显然,为了写这个答案,甚至没有读过这个问题。
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;
            }
        }
    }
}

出:

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


4
这与他想避免的问题的“对抗”部分相同。
Bdoserror

2
仅代码答案几乎与仅链接答案一样糟糕。他们根本不是真正的答案。一个好的答案包含解释和推理,可能还包含一些来源或权威。
markus

3
总比没有好,有些人认为这是最佳答案,简单直接
D4rWiNS 2014年

48

也许不像以前的答案那么优雅,但是如果您想实现几乎没有较大范围的切换用例,只需事先将范围组合成单个用例即可:

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

11
我不推荐这个。如果只运行代码,那么直觉就case 1意味着variable == 1,从长远来看,这会导致混乱和很多痛苦。如果您需要在代码中放置注释以使其可读,那么恕我直言,您做错了什么。
kraxor 2014年

21

一个面向对象的选择更换过大switchif/else结构是使用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语句都是关于副作用的,它将副作用封装在类中,以便可以对其进行管理和更好地重用,最终更像是功能语言中的模式匹配,这不是一件坏事。

我将在Github上发布对该Gist的任何更新或增强。


2
我同意,如果您有大量变量,则case语句和if if块很讨厌。如果您要进行大量的案例陈述,那么您将不会像过去那样尽善尽美。
布莱恩·穆克洛

11

根据这个问题,这是完全可能的。

只需将所有包含相同逻辑的案例放在一起,不要将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则是选择。


2
这个问题已经提供了解决方案,并询问是否有一种方法可以指定范围不用对范围内的每个值进行编码(OP需要96条case语句!)。恐怕我同意接受的答案。
波希米亚

感谢您的评论。也许见编辑。我同意这全都取决于场景,可能不是5 vs 95。
WesternGun

6

基本上:

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

如果确实需要使用开关,那是因为您需要针对特定​​范围执行各种操作。在那种情况下,是的,您将拥有混乱的代码,因为事情变得越来越复杂,只有遵循模式的事情才能很好地压缩。

切换的唯一原因是,如果您只是在测试数字切换值,则无需输入变量名。您不会打开100件事,也不会全部都做同样的事情。听起来更像是“ if”块。


4

//不兼容的代码示例

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

//兼容的解决方案

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

实际的答案值得赞许。真好
user1735921

1
谢谢user1735921
Manoj Kumar Sharma

3

从最新的Java-12版本开始,预览语言功能提供了具有相同大小写标签的多个常量

JDK功能发布中提供了该功能,以根据实际使用情况激发开发人员反馈。这可能会导致它在将来的Java SE平台中永久存在。

看起来像:

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

查看更多JEP 325:开关表达式(预览)


2

可以使用Vavr库处理此问题

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

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

当然,这只是轻微的改进,因为仍然需要明确列出所有情况。但是定义自定义谓词很容易:

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是一个表达式,因此在这里它返回类似于Runnable实例的内容,而不是直接调用方法。执行匹配后,Runnable即可执行。

有关更多详细信息,请参见官方文档


1

作为替代,您可以使用以下方法:

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

    }

或以下代码也可以

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

1

JEP 354: JDK-13中的开关表达式(预览) JEP 361: JDK-14中的开关表达式(标准)将扩展 switch语句,因此可以将其用作表达式

现在你可以:

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

    “ case L->”开关标签右侧的代码被限制为表达式,块或(为方便起见)throw语句。

  • 在每种情况下使用多个常量,以逗号分隔,
  • 而且也没有更多的价值突破

    为了从switch表达式中产生一个值,breakwith value语句被删除,转而使用一条yield语句。

开关表达式示例:

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

0

除了使用硬编码值之外,一种替代方法是在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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.