Java switch 语句多种情况

只是想弄清楚如何使用多种情况下的 Java 开关语句。下面是我正在尝试做的一个例子:

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

而不是不得不做:

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

如果可能的话,有什么想法,或者什么是好的替代方案?

404149 次浏览

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

替换过大的 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 上发布这个 要点的任何更新或增强。

第二种选择完全没问题。我不知道为什么应急人员说不可能。没关系,我经常这么做:

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

也许不像以前的一些答案那样优雅,但是如果你想要实现几个大范围的开关情况,只需要事先将范围合并到一个单一的情况:

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

基本上:

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

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

如果您只是在测试数字切换值,那么使用开关的唯一原因是节省输入变量名的时间。你不会打开100个开关,它们也不会全部做同样的事情。听起来更像是“如果”。

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

只要把所有包含相同逻辑的案例放在一起,不要把 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;
}

这是因为没有 breakcase会跳到另一个 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是选择。

可以使用 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

详情请参阅 正式文件

//不符合规定的代码示例

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

你可使用以下方法:

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


}

或者下面的代码也可以工作

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

从最近的 java-12版本中可以看到,在 预览语言功能预览语言功能中可以使用同一个 case 标签中的多个常量

在 JDK 特性版本中,可以根据实际使用情况激发开发人员的反馈; 这可能导致它在未来的 Java SE 平台中成为永久性的。

看起来像是:

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

详见 JEP 325: 开关表达式(预览)

不使用硬编码值的一种替代方法是在 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;
}

JEP 354: JDK-13中的 Switch Expressions (Preview) 和 JDK-14中的 JEP 361: 开关表达式(标准) 将扩展 开关语句,使其可以用作 表情

现在你可以:

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

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

  • 每种情况下使用多个常量,用逗号分隔,
  • 也没有更多的值 休息:

    为了从 switch 表达式生成一个值,删除带 value 语句的 break,使其支持 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);
}
}


这可以通过 爪哇14中的交换机增强来实现。下面是一个相当直观的例子,说明如何能够实现相同的目标。

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

我找到了一个解决这个问题的办法... 我们可以使用多种条件在开关情况下在爪哇。.但需要多个开关箱。.

public class MultiCSwitchTest {
public static void main(String[] args) {
int i = 209;
int a = 0;
switch (a = (i>=1 && i<=100) ? 1 : a){
case 1:
System.out.println ("The Number is Between 1 to 100 ==> " + i);
break;
default:
switch (a = (i>100 && i<=200) ? 2 : a) {
case 2:
System.out.println("This Number is Between 101 to 200 ==> " + i);
break;
        

default:
switch (a = (i>200 && i<=300) ? 3 : a) {
case 3:
System.out.println("This Number is Between 201 to 300 ==> " + i);
break;
                

default:
// You can make as many conditions as you want;
break;
}
}
        

}
}
}