Pernyataan switch Java banyak kasus

118

Hanya mencoba mencari cara menggunakan banyak kasus untuk pernyataan switch Java. Inilah contoh dari apa yang saya coba lakukan:

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

dibandingkan harus melakukan:

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

Adakah ide jika ini memungkinkan, atau alternatif apa yang baik?

FunJavaCode
sumber
12
Sepertinya Anda menggunakan bilangan bulat jadi saya kira jika Anda tahu bahwa rentang Anda berukuran tetap, Anda selalu dapat melakukan switch (variabel / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Jawaban:

80

Sayangnya, hal itu tidak mungkin dilakukan di Jawa. Anda harus menggunakan if-elsepernyataan.

Bala R
sumber
1
@FunJavaCode AFAIK Anda dapat melakukannya di vb.net. Inilah contohnya
Bala R
1
@YuryLitvinov, apakah Anda ingin menjelaskan mengapa menurut Anda itu tidak benar?
Bala R
1
Jawaban saya membuktikan bahwa ini tidak benar, ini adalah OO dan merupakan pola yang diketahui dan diterima untuk menangani masalah yang tepat ini di antara orang lain yang memerlukan banyak if/elseif/elsepernyataan bersarang , terlepas dari bahasanya.
1
Dimungkinkan untuk mendapatkan kondisi ATAU dalam kasus SWITCH dengan menggunakan tautan ini ... silakan periksa: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha
4
bro, kemungkinan penggunaannya dapat menulis beberapa kasus tanpa menggunakan break dan di akhir kasus Anda dapat menulis logika Anda seperti: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2
85

Opsi kedua baik-baik saja. Saya tidak yakin mengapa seorang responden mengatakan itu tidak mungkin. Ini bagus, dan saya melakukan ini sepanjang waktu:

switch (variable)
{
    case 5:
    case 6:
    etc.
    case 100:
        doSomething();
    break;
}
Dave
sumber
50
Penanya berkata lakukan ini "versus" melakukan ini. Dia mengerti bahwa apa yang Anda cantumkan itu valid, dia mencoba melakukan hal pertama BUKAN itu.
Blaine Mucklow
45
Saya minta maaf, tetapi saya tidak melihat bagaimana mencantumkan 95 kasus berturut-turut yang mengarah ke hal yang sama adalah solusi untuk apa pun. Jika saya menemukan itu dalam kode apa pun, saya akan melacak mereka, menculik mereka, mengirimkannya secara pribadi ke GLaDOS, dan berharap dia memberi mereka urutan tes paling mematikan yang dapat dia temukan.
animuson
1
@animuson di atasnya, dia mendapat suara positif 60 kali .... go figure. Saya datang ke sini karena saya tidak ingin melakukan hal yang TEPAT dia jawab
killjoy
Downvoting karena ini tidak menjawab pertanyaan tersebut. . .heck, rupanya, pertanyaan itu bahkan tidak dibaca untuk menulis jawaban ini.
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;
            }
        }
    }
}

Di luar:

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

P. Waksman
sumber
4
Itu sama dengan bagian "versus" dari pertanyaannya, yang ingin dia hindari.
Bdoserror
2
Jawaban hanya kode hampir seburuk jawaban link saja. Itu sama sekali bukan jawaban. Jawaban yang bagus berisi penjelasan dan alasan dan mungkin beberapa sumber atau otoritas.
markus
3
Ini lebih baik daripada tidak sama sekali, beberapa orang menganggap itu sebagai jawaban terbaik, sederhana dan langsung
D4rWiNS
48

Mungkin tidak seanggun beberapa jawaban sebelumnya, tetapi jika Anda ingin mendapatkan sakelar sakelar dengan beberapa rentang besar, cukup gabungkan rentang ke satu kasing sebelumnya:

// 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.
} 
Santtu Kähkönen
sumber
11
Saya tidak akan merekomendasikan ini. Jika Anda hanya menjalankan kode, Anda akan memiliki intuisi yang case 1berarti variable == 1, yang menyebabkan kebingungan dan banyak rasa sakit dalam jangka panjang. Jika Anda perlu memberi komentar di kode Anda agar dapat dibaca, maka Anda melakukan kesalahan IMHO.
kraxor
21

Salah satu opsi Berorientasi Objek untuk menggantikan konstruksi switchdan if/elsekonstruksi yang terlalu besar adalah dengan menggunakan a Chain of Responsibility Patternuntuk memodelkan pengambilan keputusan.

Pola Rantai Tanggung Jawab

Pola rantai tanggung jawab memungkinkan pemisahan sumber permintaan dari memutuskan mana dari sejumlah besar penangan yang berpotensi untuk permintaan tersebut yang harus menindaklanjutinya. Kelas yang mewakili peran rantai menyalurkan permintaan dari sumber di sepanjang daftar penangan sampai penangan menerima permintaan dan menindaklanjutinya.

Berikut adalah contoh implementasi yang juga Type Safe menggunakan Generik.

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

    }
}

Ini hanya orang cepat yang saya siapkan dalam beberapa menit, implementasi yang lebih canggih mungkin memungkinkan beberapa jenis Command Patternuntuk disuntikkan ke dalam Casecontoh implementasi untuk membuatnya lebih seperti panggilan kembali gaya IoC.

Setelah hal yang menyenangkan tentang pendekatan ini adalah bahwa pernyataan Switch / Case adalah tentang pengaruh samping, ini merangkum efek samping di Kelas sehingga dapat dikelola, dan digunakan kembali dengan lebih baik, akhirnya menjadi lebih seperti Pencocokan Pola dalam bahasa Fungsional dan itu bukanlah hal yang buruk.

Saya akan memposting pembaruan atau peningkatan apa pun ke Gist ini di Github.


sumber
2
Saya setuju, pernyataan kasus dan blok if besar buruk jika Anda memiliki banyak variabel. Jika Anda melakukan banyak pernyataan kasus maka Anda tidak menggunakan prinsip OO sebaik yang Anda bisa.
Blaine Mucklow
11

Menurut pertanyaan ini , sangat mungkin.

Gabungkan saja semua kasus yang berisi logika yang sama, dan jangan letakkan di breakbelakangnya.

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

Itu karena casetanpa breakakan melompat ke yang lain casesampai breakatau return.

EDIT:

Membalas komentar, jika kita benar-benar memiliki 95 nilai dengan logika yang sama, tetapi jumlah kasus yang jauh lebih kecil dengan logika berbeda, kita dapat melakukan:

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

Jika Anda membutuhkan kontrol yang lebih baik, if-elseitu pilihannya.

WesternGun
sumber
2
Pertanyaannya sudah menawarkan ini sebagai solusi dan menanyakan apakah ada cara untuk menentukan rentang tanpa harus mengkodekan setiap nilai dalam rentang (OP akan membutuhkan 96 casepernyataan!). Saya khawatir saya setuju dengan jawaban yang diterima.
Bohemian
Terima kasih atas komentarnya. Lihat edit, mungkin. Saya setuju bahwa semuanya tergantung pada skenario, dan 5 vs 95 mungkin bukan masalahnya.
WesternGun
6

Pada dasarnya:

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

Jika Anda benar-benar perlu menggunakan sakelar, itu karena Anda perlu melakukan berbagai hal untuk rentang tertentu. Dalam hal ini, ya, Anda akan mendapatkan kode yang berantakan, karena hal-hal menjadi rumit dan hanya hal-hal yang mengikuti pola yang akan dikompres dengan baik.

Satu-satunya alasan pengalihan adalah untuk menghemat pengetikan nama variabel jika Anda hanya menguji nilai pengalihan numerik. Anda tidak akan mengaktifkan 100 hal, dan semuanya tidak akan melakukan hal yang sama. Kedengarannya lebih seperti potongan 'jika'.

Michael Kozakewich
sumber
4

// Contoh Kode yang Tidak Sesuai

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

// Solusi yang Sesuai

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();
}
Manoj Kumar Sharma
sumber
Jawaban sebenarnya yang patut diacungi jempol. Bagus.
pengguna1735921
3

Dari rilis java-12 terakhir, beberapa konstanta dalam label kasus yang sama tersedia dalam fitur bahasa pratinjau

Ini tersedia dalam rilis fitur JDK untuk memancing umpan balik pengembang berdasarkan penggunaan dunia nyata; hal ini dapat membuatnya menjadi permanen di Platform Java SE di masa mendatang.

Sepertinya:

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

Lihat JEP 325 lainnya: Beralih Ekspresi (Pratinjau)

Ruslan
sumber
2

Hal ini dimungkinkan untuk menangani ini menggunakan perpustakaan Vavr

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

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

Hal ini tentunya hanya sedikit perbaikan karena semua kasus masih perlu dicantumkan secara eksplisit. Tetapi mudah untuk menentukan predikat khusus:

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

Kecocokan adalah ekspresi jadi di sini ia mengembalikan sesuatu seperti Runnablecontoh alih-alih memanggil metode secara langsung. Setelah pertandingan dilakukan Runnabledapat dieksekusi.

Untuk detail lebih lanjut silakan lihat dokumentasi resmi .

hgrey
sumber
1

untuk alternatif Anda bisa menggunakan seperti di bawah ini:

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

    }

atau kode berikut juga berfungsi

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

JEP 354: Switch Expressions (Preview) di JDK-13 dan JEP 361: Switch Expressions (Standard) di JDK-14 akan memperluas pernyataan switch sehingga dapat digunakan sebagai ekspresi .

Sekarang kamu bisa:

  • langsung menetapkan variabel dari ekspresi sakelar ,
  • gunakan bentuk baru dari label sakelar (case L -> ):

    Kode di sebelah kanan label sakelar "case L ->" dibatasi menjadi ekspresi, blok, atau (untuk kenyamanan) pernyataan lemparan.

  • gunakan beberapa konstanta per kasus, dipisahkan dengan koma,
  • dan juga tidak ada lagi jeda nilai :

    Untuk menghasilkan nilai dari ekspresi sakelar, pernyataan breakwith value dijatuhkan untuk mendukung yieldpernyataan.

Contoh ganti ekspresi:

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);
  }
}
Iskuskov Alexander
sumber
0

Salah satu alternatif daripada menggunakan nilai hard-coded bisa menggunakan pemetaan jangkauan pada pernyataan switch sebagai gantinya:

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;
}
guilhebl.dll
sumber