คำสั่งสวิตช์ Java หลายกรณี


118

เพียงแค่พยายามหาวิธีใช้หลาย ๆ กรณีสำหรับคำสั่งสวิตช์ Java นี่คือตัวอย่างของสิ่งที่ฉันพยายามทำ:

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

เมื่อเทียบกับต้องทำ:

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

ความคิดใด ๆ ถ้าเป็นไปได้หรือทางเลือกที่ดีคืออะไร?


12
ดูเหมือนว่าคุณกำลังใช้จำนวนเต็มดังนั้นฉันคิดว่าถ้าคุณรู้ว่าช่วงของคุณมีขนาดคงที่คุณสามารถเปลี่ยนได้ตลอดเวลา (ตัวแปร / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

คำตอบ:


80

น่าเศร้าที่ไม่สามารถทำได้ใน Java คุณจะต้องหันไปใช้if-elseงบ


1
@FunJavaCode AFAIK คุณสามารถทำได้ใน vb.net นี่คือตัวอย่าง
Bala R

1
@YuryLitvinov คุณต้องการขยายว่าทำไมคุณถึงคิดว่ามันไม่ถูกต้อง?
Bala R

1
คำตอบของฉันพิสูจน์ได้ว่าไม่ถูกต้องเป็น OO และเป็นรูปแบบที่ทราบและเป็นที่ยอมรับในการจัดการกับปัญหาที่แน่นอนนี้และเป็นรูปแบบอื่น ๆ ที่ต้องใช้if/elseif/elseคำสั่งซ้อนกันโดยไม่คำนึงถึงภาษา

1
เป็นไปได้ที่จะรับเงื่อนไข OR ในเคส SWITCH โดยใช้ลิงค์เหล่านี้ ... โปรดตรวจสอบ: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha

4
การใช้งานที่เป็นไปได้สามารถเขียนได้หลายกรณีโดยไม่ต้องใช้ตัวแบ่งและในตอนท้ายของกรณีคุณสามารถเขียนตรรกะของคุณเช่น: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2

85

ตัวเลือกที่สองสมบูรณ์ดี ฉันไม่แน่ใจว่าทำไมผู้ตอบกลับบอกว่าเป็นไปไม่ได้ ไม่เป็นไรและฉันทำสิ่งนี้ตลอดเวลา:

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

50
ผู้ถามบอกว่าทำแบบนี้ "เทียบ" เขาเข้าใจดีว่าสิ่งที่คุณระบุนั้นถูกต้องเขาพยายามทำสิ่งแรกแทนสิ่งนั้น
Blaine Mucklow

45
ฉันขอโทษ แต่ฉันไม่เห็นว่าการแสดงรายการ 95 คดีในแถวเดียวกันนั้นเป็นวิธีแก้ปัญหาอย่างไร หากฉันพบว่าในรหัสใด ๆ ฉันจะติดตามพวกเขาลักพาตัวพวกเขาส่งมอบให้ GLaDOS เป็นการส่วนตัวและหวังว่าเธอจะให้ลำดับการทดสอบที่อันตรายที่สุดที่เธอสามารถพบได้
animuson

1
@animuson ซึ่งเขาได้รับการโหวตถึง 60 ครั้ง .... ไปเลย ฉันมาที่นี่เพราะฉันไม่ต้องการทำสิ่งที่แน่นอนที่เขาตอบ
killjoy

การลงคะแนนเนื่องจากไม่สามารถตอบคำถามได้ . เห็นได้ชัดว่าคำถามไม่ได้ถูกอ่านเพื่อเขียนคำตอบนี้
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
ดีกว่าไม่มีอะไรบางคนใช้คำตอบนั้นเป็นคำตอบที่ดีที่สุดง่ายและตรง
ประเด็น

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ซึ่งนำไปสู่ความสับสนและความเจ็บปวดอย่างมากในระยะยาว หากคุณต้องการใส่ความคิดเห็นในโค้ดของคุณเพื่อให้อ่านได้แสดงว่าคุณทำบางอย่างผิดพลาด IMHO
kraxor

21

อ็อพชัน Object Oriented หนึ่งตัวเพื่อแทนที่ขนาดใหญ่เกินไปswitchและif/elseสร้างคือการใช้Chain of Responsibility Patternแบบจำลองการตัดสินใจ

รูปแบบความรับผิดชอบของห่วงโซ่

รูปแบบห่วงโซ่ความรับผิดชอบช่วยให้สามารถแยกแหล่งที่มาของคำขอออกจากการตัดสินใจว่าตัวจัดการใดในจำนวนมากที่มีศักยภาพสำหรับคำขอนั้นควรดำเนินการ คลาสที่เป็นตัวแทนของ chain role จะแชนเนลคำร้องขอจากซอร์สตามรายการตัวจัดการจนกว่าตัวจัดการจะยอมรับคำร้องขอและดำเนินการ

นี่คือตัวอย่างการใช้งานที่เป็น Type Safe โดยใช้ Generics

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 นั้นเกี่ยวกับผลข้างเคียงสิ่งนี้จะสรุปผลข้างเคียงใน Classes เพื่อให้สามารถจัดการและนำกลับมาใช้ใหม่ได้ดีขึ้นมันจะเหมือนกับ Pattern Matching ในภาษาที่ใช้งานได้และ นั่นไม่ใช่เรื่องเลวร้าย

ฉันจะโพสต์การอัปเดตหรือการปรับปรุงใด ๆ ในGistนี้บน Github


2
ฉันเห็นด้วยคำชี้แจงกรณีและบล็อกใหญ่ถ้าคุณมีตัวแปรจำนวนมาก หากคุณกำลังทำคำชี้แจงกรณีจำนวนมากแสดงว่าคุณไม่ได้ใช้หลักการ OO เช่นเดียวกับที่คุณเป็น
Blaine Mucklow

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 จะต้องใช้caseคำสั่ง96 !) ฉันกลัวว่าฉันจะเห็นด้วยกับคำตอบที่ยอมรับ
โบฮีเมียน

ขอบคุณสำหรับความคิดเห็น ดูแก้ไขอาจจะ ฉันยอมรับว่าทุกอย่างขึ้นอยู่กับสถานการณ์และ 5 vs 95 อาจไม่เป็นเช่นนั้น
WesternGun

6

โดยทั่วไป:

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

หากคุณจำเป็นต้องใช้สวิตช์จริงๆอาจเป็นเพราะคุณต้องทำสิ่งต่างๆในบางช่วง ในกรณีนั้นใช่คุณจะมีโค้ดที่ยุ่งเหยิงเพราะสิ่งต่าง ๆ เริ่มซับซ้อนและมีเพียงสิ่งเดียวที่เป็นไปตามรูปแบบเท่านั้นที่จะบีบอัดได้ดี

เหตุผลเดียวที่สวิตช์คือประหยัดในการพิมพ์ชื่อตัวแปรหากคุณแค่ทดสอบค่าการสลับตัวเลข คุณจะไม่เปิด 100 สิ่งและจะไม่ทำสิ่งเดียวกันทั้งหมด ฟังดูเหมือนชิ้นส่วน 'ถ้า' มากกว่า


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

3

จาก java-12 รีลีสล่าสุดหลายค่าคงที่ในเลเบลเคสเดียวกันมีให้ใช้งานในคุณสมบัติภาษาตัวอย่าง

มีอยู่ในรุ่นคุณลักษณะ JDK เพื่อกระตุ้นความคิดเห็นของนักพัฒนาตามการใช้งานจริง ซึ่งอาจนำไปสู่การเป็นแพลตฟอร์ม Java SE ในอนาคตอย่างถาวร

ดูเหมือนว่า:

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

ดูเพิ่มเติม JEP 325: Switch Expressions (ตัวอย่าง)


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: Switch Expressions (Preview)ใน JDK-13 และ JEP 361: Switch Expressions (Standard)ใน JDK-14 จะขยายคำสั่ง switchเพื่อให้สามารถใช้เป็นนิพจน์ได้ได้

ตอนนี้คุณสามารถ:

  • โดยตรงตัวแปรกำหนดจากการแสดงออกของสวิทช์ ,
  • ใช้ป้ายกำกับสวิตช์รูปแบบใหม่ ( case L ->):

    รหัสทางด้านขวาของป้ายสวิตช์ "case L ->" ถูก จำกัด ให้เป็นนิพจน์บล็อกหรือ (เพื่อความสะดวก) คำสั่ง Throw

  • ใช้ค่าคงที่หลายค่าต่อกรณีโดยคั่นด้วยเครื่องหมายจุลภาค
  • และไม่มีการแบ่งมูลค่าเพิ่มเติม:

    เพื่อให้ได้ค่าจากนิพจน์สวิตช์คำสั่ง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

ทางเลือกหนึ่งแทนที่จะใช้ค่าฮาร์ดโค้ดอาจใช้การแมปช่วงบนคำสั่งสวิตช์แทน:

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.