Câu lệnh chuyển đổi Java nhiều trường hợp


118

Chỉ đang cố gắng tìm ra cách sử dụng nhiều trường hợp cho một câu lệnh Java switch. Đây là một ví dụ về những gì tôi đang cố gắng làm:

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

so với việc phải làm:

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

Bất kỳ ý tưởng nào nếu điều này có thể, hoặc một giải pháp thay thế tốt là gì?


12
Có vẻ như bạn đang sử dụng số nguyên nên tôi cho rằng nếu bạn biết rằng phạm vi của mình có kích thước cố định, bạn luôn có thể thực hiện chuyển đổi (biến / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Câu trả lời:


80

Đáng buồn thay, nó không thể thực hiện được trong Java. Bạn sẽ phải sử dụng các if-elsecâu lệnh.


1
@FunJavaCode AFAIK bạn có thể làm điều đó trong vb.net. Đây là một ví dụ
Bala R

1
@YuryLitvinov bạn có muốn mở rộng lý do tại sao bạn cho rằng nó không chính xác không?
Bala R

1
Câu trả lời của tôi chứng minh điều này là không chính xác, đó là OO và là một mô hình được biết đến và được chấp nhận để giải quyết vấn đề chính xác này trong khi các câu trả lời khác yêu cầu nhiều if/elseif/elsecâu lệnh lồng nhau , bất kể ngôn ngữ.

1
Có thể lấy điều kiện HOẶC trong trường hợp SWITCH bằng cách sử dụng liên kết này ... vui lòng kiểm tra nó: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha

4
bạn có thể sử dụng nó có thể viết nhiều trường hợp mà không cần sử dụng break và ở cuối trường hợp, bạn có thể viết logic của bạn như: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2

85

Lựa chọn thứ hai là hoàn toàn ổn. Tôi không chắc tại sao một người trả lời lại nói rằng điều đó là không thể. Điều này là tốt, và tôi làm điều này mọi lúc:

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

50
Người hỏi nói làm điều này "so với" làm điều này. Anh ấy hiểu rằng những gì bạn liệt kê là hợp lệ, anh ấy đang cố gắng làm điều đầu tiên INSTEAD về điều đó.
Blaine Mucklow

45
Tôi xin lỗi, nhưng tôi không thấy liệt kê 95 trường hợp liên tiếp xảy ra cùng một vấn đề là giải pháp cho bất cứ điều gì. Nếu tôi gặp điều đó trong bất kỳ đoạn mã nào, tôi sẽ truy tìm chúng, bắt cóc chúng, giao chúng đến GLaDOS và hy vọng cô ấy cung cấp cho họ chuỗi bài kiểm tra nguy hiểm nhất mà cô ấy có thể tìm thấy.
animuson

1
@animuson trên hết, anh ấy được ủng hộ 60 lần .... đi con số. Tôi đến đây coz tôi không muốn làm điều CHÍNH XÁC ông trả lời
killjoy

Từ chối vì điều này không trả lời câu hỏi. . . kiểm tra, rõ ràng là câu hỏi thậm chí còn không được đọc để viết câu trả lời này.
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;
            }
        }
    }
}

Ngoài:

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
Điều đó cũng giống như phần "so với" trong câu hỏi của anh ấy, điều mà anh ấy muốn tránh.
Bdoserror

2
Câu trả lời chỉ có mã gần như tệ như câu trả lời chỉ liên kết. Chúng thực sự không phải là câu trả lời. Một câu trả lời tốt bao gồm giải thích và lý luận và có thể là một số nguồn hoặc cơ quan.
markus

3
Tốt hơn là không có gì, một số người coi đó là câu trả lời tốt nhất, đơn giản và trực tiếp
D4rWiNS

48

Có thể không thanh lịch như một số câu trả lời trước đây, nhưng nếu bạn muốn đạt được các trường hợp chuyển đổi với ít phạm vi lớn, chỉ cần kết hợp các phạm vi với một trường hợp trước:

// 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
Tôi không khuyên bạn nên điều này. Nếu bạn chỉ chạy qua mã, bạn sẽ có trực giác case 1có nghĩa là variable == 1, điều này dẫn đến sự nhầm lẫn và rất nhiều đau đớn về lâu dài. Nếu bạn cần đặt nhận xét trong mã của mình để làm cho mã có thể đọc được, thì bạn đã làm sai điều gì đó IMHO.
kraxor

21

Một tùy chọn Hướng đối tượng để thay thế các cấu trúc switchvà quá lớn if/elselà sử dụng một Chain of Responsibility Patternđể mô hình hóa việc ra quyết định.

Mô hình chuỗi trách nhiệm

Mô hình chuỗi trách nhiệm cho phép tách nguồn của một yêu cầu khỏi việc quyết định xem người xử lý nào trong số lượng lớn có khả năng xử lý cho yêu cầu sẽ thực hiện nó. Lớp đại diện cho vai trò chuỗi phân luồng các yêu cầu từ nguồn cùng với danh sách các trình xử lý cho đến khi một trình xử lý chấp nhận yêu cầu và thực hiện nó.

Đây là một ví dụ triển khai cũng là Kiểu An toàn bằng cách sử dụng 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);

    }
}

Đây chỉ là một người đàn ông rơm nhanh chóng mà tôi đã sử dụng trong vài phút, một cách thực hiện phức tạp hơn có thể cho phép một số loại Command Patternđược tiêm vàoCase triển khai để làm cho nó giống như kiểu IoC gọi lại.

Một điều thú vị về cách tiếp cận này là các câu lệnh Switch / Case đều có tác động phụ, điều này gói gọn các tác dụng phụ trong các Lớp để chúng có thể được quản lý và sử dụng lại tốt hơn, nó sẽ giống như Khớp mẫu trong ngôn ngữ Chức năng và đó không phải là một điều xấu.

Tôi sẽ đăng bất kỳ cập nhật hoặc cải tiến nào cho Gist này trên Github.


2
Tôi đồng ý, các câu lệnh trường hợp và các khối if lớn là khó chịu nếu bạn có một lượng lớn các biến. Nếu bạn đang thực hiện nhiều câu hỏi tình huống thì bạn đang không sử dụng các nguyên tắc OO tốt như bạn có thể.
Blaine Mucklow,

11

Theo câu hỏi này , nó hoàn toàn có thể.

Chỉ đặt tất cả các trường hợp có cùng logic với nhau và không đặt breaksau chúng.

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

Đó là bởi vì casekhông có breaksẽ chuyển sang cái khác casecho đến khi breakhoặc return.

BIÊN TẬP:

Trả lời nhận xét, nếu chúng ta thực sự có 95 giá trị với cùng một logic, nhưng số lượng trường hợp có logic khác nhau nhỏ hơn, chúng ta có thể làm:

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

Nếu bạn cần kiểm soát tốt hơn, if-elselà sự lựa chọn.


2
Câu hỏi đã đưa ra giải pháp này và hỏi liệu có cách nào để chỉ định một phạm vi mà không cần phải viết mã mọi giá trị trong phạm vi hay không (OP sẽ yêu cầu 96 casecâu lệnh!). Tôi e rằng tôi đồng ý với câu trả lời được chấp nhận.
Bohemian

Cảm ơn vì đã bình luận. Xem chỉnh sửa, có thể. Tôi đồng ý rằng tất cả phụ thuộc vào kịch bản, và 5 vs 95 có thể không đúng.
WesternGun

6

Về cơ bản:

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

Nếu bạn thực sự cần sử dụng một công tắc, đó là vì bạn cần thực hiện nhiều việc khác nhau cho một số phạm vi nhất định. Trong trường hợp đó, vâng, bạn sẽ có những đoạn mã lộn xộn, bởi vì mọi thứ đang trở nên phức tạp và chỉ những thứ tuân theo các mẫu mới được nén tốt.

Lý do duy nhất cho việc chuyển đổi là tiết kiệm việc nhập tên biến nếu bạn chỉ đang thử nghiệm các giá trị chuyển đổi số. Bạn sẽ không bật 100 thứ và không phải tất cả chúng sẽ làm cùng một thứ. Điều đó nghe giống như một đoạn 'nếu'.


4

// Ví dụ về mã không tuân thủ

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

// Giải pháp Tuân thủ

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

Câu trả lời thực sự đáng đồng tình. Đẹp.
user1735921 19/01

3

Từ bản phát hành java-12 cuối cùng, nhiều hằng số trong cùng một nhãn trường hợp có sẵn trong tính năng ngôn ngữ xem trước

Nó có sẵn trong bản phát hành tính năng JDK để kích thích phản hồi của nhà phát triển dựa trên việc sử dụng trong thế giới thực; điều này có thể dẫn đến việc nó trở thành vĩnh viễn trong Nền tảng Java SE trong tương lai.

Nó có vẻ như:

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

Xem thêm JEP 325: Chuyển biểu thức (Xem trước)


2

Có thể xử lý điều này bằng thư viện Vavr

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

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

Tất nhiên đây chỉ là cải thiện nhỏ vì tất cả các trường hợp vẫn cần được liệt kê rõ ràng. Nhưng thật dễ dàng để xác định vị từ tùy chỉnh:

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

Đối sánh là một biểu thức vì vậy ở đây nó trả về một cái gì đó giống như Runnablethể hiện thay vì gọi trực tiếp các phương thức. Sau khi trận đấu được thực hiện Runnablecó thể được thực hiện.

Để biết thêm chi tiết, vui lòng xem tài liệu chính thức .


1

để thay thế bạn có thể sử dụng như sau:

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

    }

hoặc mã sau đây cũng hoạt động

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

1

JEP 354: Biểu thức chuyển đổi (Xem trước) trong JDK-13 và JEP 361: Biểu thức chuyển đổi (Chuẩn) trong JDK-14 sẽ mở rộng câu lệnh switch để nó có thể được sử dụng như một biểu thức .

Bây giờ bạn có thể:

  • chỉ định trực tiếp biến từ biểu thức switch ,
  • sử dụng hình thức mới của nhãn chuyển đổi (case L -> ):

    Mã ở bên phải của nhãn chuyển đổi "case L ->" bị hạn chế là một biểu thức, một khối hoặc (để thuận tiện) một câu lệnh ném.

  • sử dụng nhiều hằng số cho mỗi trường hợp, được phân tách bằng dấu phẩy,
  • và cũng không có thêm các ngắt giá trị :

    Để mang lại một giá trị từ một biểu thức switch, breakcâu lệnh with value được loại bỏ để có lợi cho một yieldcâu lệnh.

Ví dụ về chuyển đổi biểu thức:

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

Một giải pháp thay thế thay vì sử dụng các giá trị được mã hóa cứng có thể là sử dụng ánh xạ phạm vi trên câu lệnh 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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.