Java anahtar ifadesi birden çok durum


118

Sadece bir Java switch ifadesi için birden fazla durumun nasıl kullanılacağını anlamaya çalışıyorum. İşte yapmaya çalıştığım şeyin bir örneği:

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

yapmak zorunda olmak yerine:

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

Bu mümkünse veya iyi bir alternatif ne olabilir?


12
Görünüşe göre tamsayılar kullanıyorsunuz, bu nedenle aralıklarınızın sabit bir boyutta olduğunu biliyorsanız her zaman geçiş yapabilirsiniz (değişken / FIXED_SIZE_OF_RANGE) {case 0: ... default: break; }
paulrehkugler

Yanıtlar:


80

Ne yazık ki Java'da bu mümkün değil. if-elseİfadeleri kullanmak zorunda kalacaksınız .


1
@FunJavaCode AFAIK bunu vb.net'te yapabilirsiniz. İşte bir örnek
Bala R

1
@YuryLitvinov, neden yanlış olduğunu düşündüğünüzü genişletmek ister misiniz?
Bala R

1
Cevabım bunun yanlış olduğunu, OO olduğunu kanıtlıyor if/elseif/elseve dilden bağımsız olarak birçok iç içe ifade gerektiren diğerlerinin yanı sıra bu problemle başa çıkmak için bilinen ve kabul edilen bir model .

1
Bu bağlantıyı kullanarak SWITCH durumunda OR koşulunu elde etmek mümkündür ... plz kontrol edin: - stackoverflow.com/a/16706729/3946958
Ravindra Kushwaha

4
bro, olası kullanımı break kullanmadan birden fazla durum yazabilir ve durumun sonunda mantığınızı şöyle yazabilirsiniz: case some_value: case some_value: case some_value: you_logic_goes_here break;
anoopbryan2

85

İkinci seçenek tamamen iyidir. Bir cevapçının neden mümkün olmadığını söylediğinden emin değilim. Bu iyi ve bunu her zaman yapıyorum:

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

50
Soruyu soran kişi bunu "karşı" yaparak yap dedi. Listelediğinizin geçerli olduğunu anlıyor, onun YERİNE ilk şeyi yapmaya çalışıyordu.
Blaine Mucklow

45
Üzgünüm, ama aynı şeye giden 95 davayı arka arkaya sıralamak nasıl herhangi bir şeye çözüm olur anlamıyorum. Bununla herhangi bir kodda karşılaşsaydım, onları bulur, kaçırır, kişisel olarak GLaDOS'a teslim eder ve bulabileceği en ölümcül test dizisini onlara vermesini umardım.
animuson

1
@animuson, bunun üzerine 60 kez ek oy alıyor .... git şekil. Buraya geldim çünkü onun cevapladığı TAM şeyi yapmak istemedim
killjoy

Bu soruyu yanıtlamadığı için olumsuz oy kullanmak. . .heck, görünüşe göre, bu cevabı yazmak için soru okunmadı bile.
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;
            }
        }
    }
}

Dışarı:

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
Bu, sorusunun kaçınmak istediği "karşı" kısmıyla aynı.
Bdoserror

2
Yalnızca kod yanıtları neredeyse yalnızca bağlantı yanıtları kadar kötüdür. Gerçekten cevap değiller. İyi bir cevap, açıklamalar ve muhakeme ve muhtemelen bazı kaynaklar veya otoriteler içerir.
markus

3
Hiç yoktan iyidir, bazı insanlar bunu en iyi cevap olarak kabul eder, basit ve doğrudan
D4rWiNS

48

Belki önceki cevaplar kadar zarif olmayabilir, ancak birkaç büyük aralıklı anahtar durumları elde etmek istiyorsanız, önceden aralıkları tek bir durumla birleştirmeniz yeterlidir:

// 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
Bunu tavsiye etmem. Sadece kod üzerinden çalıştırırsanız, sezgi sahip olacağı case 1anlamına gelir variable == 1, karışıklığa neden olur ve uzun vadede çok fazla acı. Okunabilir hale getirmek için kodunuza yorum eklemeniz gerekiyorsa, o zaman yanlış bir şey yaptınız IMHO.
kraxor

21

Aşırı büyük switchve if/elseyapıları değiştirmek için bir Nesne Yönelimli seçenek Chain of Responsibility Pattern, karar vermeyi modellemek için a kullanmaktır .

Sorumluluk Zinciri Modeli

Sorumluluk zinciri modeli, bir talebin kaynağının, talep için potansiyel olarak çok sayıda işleyiciden hangisinin onu gerçekleştirmesi gerektiğine karar vermekten ayrılmasına izin verir. Zincir rolünü temsil eden sınıf, bir işleyici isteği kabul edene ve eylemi gerçekleştirene kadar kaynaktan gelen istekleri işleyiciler listesi boyunca kanalize eder.

Burada, Generics kullanılarak Güvenli Tip olan bir örnek uygulama verilmiştir.

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

    }
}

Bu, birkaç dakika içinde kırbaçladığım hızlı bir saman adam, daha karmaşık bir uygulama Command Pattern, Caseuygulama örneklerine bir tür IoC geri çağırma tarzı eklemek için bir tür enjekte edilmesine izin verebilir .

Bir kez bu yaklaşımla ilgili güzel olan şey, Switch / Case ifadelerinin tamamen yan etkilerle ilgili olmasıdır, bu, Sınıflardaki yan etkileri kapsayarak daha iyi yönetilebilir ve daha iyi yeniden kullanılabilir, daha çok İşlevsel bir dilde Desen Eşleştirme gibi olur ve bu kötü bir şey değil.

Bu Gist ile ilgili tüm güncellemeleri veya geliştirmeleri Github'da yayınlayacağım.


2
Katılıyorum, vaka ifadeleri ve büyük miktarda değişkeniniz varsa bloklar kötü ise büyük. Çok fazla vaka açıklaması yapıyorsanız, OO ilkelerini olabildiğince iyi kullanmıyorsunuz demektir.
Blaine Mucklow

11

Bu soruya göre tamamen mümkün.

Sadece aynı mantığı içeren tüm vakaları bir araya getirin ve breakarkalarına koymayın .

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

Bunun nedeni, caseolmadan veya olana kadar breakbaşka birine atlayacak olmasıdır .casebreakreturn

DÜZENLE:

Yorumu yanıtlayarak, gerçekten aynı mantığa sahip 95 değerimiz varsa, ancak farklı mantıkla daha az sayıda vaka varsa, şunları yapabiliriz:

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

Daha hassas kontrole ihtiyacınız varsa if-else, seçim budur.


2
Soru zaten bunu bir çözüm olarak sunuyor ve aralıktaki her değeri kodlamak zorunda kalmadan bir aralık belirtmenin bir yolu olup olmadığını soruyor (OP, 96 caseifade gerektirir !). Korkarım kabul edilen cevaba katılıyorum.
Bohemian

Yorum için teşekkürler. Düzenlemeye bakın belki. Her şeyin senaryoya bağlı olduğuna katılıyorum ve 5'e 95 geçerli olmayabilir.
WesternGun

6

Temelde:

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

Gerçekten bir anahtar kullanmanız gerekiyorsa, bunun nedeni belirli aralıklar için çeşitli şeyler yapmanız gerekmesidir. Bu durumda, evet, karmaşık bir kodunuz olacak çünkü işler karmaşıklaşıyor ve sadece kalıpları takip eden şeyler iyi bir şekilde sıkışacak.

Bir anahtarın tek nedeni, yalnızca sayısal anahtarlama değerleri için test yapıyorsanız, değişken adını yazmaktan tasarruf etmektir. 100 şeyi değiştirmeyeceksiniz ve hepsi aynı şeyi yapmayacak. Bu daha çok bir 'eğer' parçası gibi geliyor.


4

// Uyumlu Olmayan Kod Örneği

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

// Uyumlu Çözüm

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

Beğenmeyi hak eden asıl Cevap. Güzel.
user1735921

3

Son java-12 sürümünden itibaren aynı durum etiketi içinde birden fazla sabit dil önizleme özelliğinde mevcuttur

Gerçek dünya kullanımına dayalı olarak geliştirici geri bildirimlerini tetiklemek için bir JDK özellik sürümünde mevcuttur; bu, gelecekteki bir Java SE Platformunda kalıcı hale gelmesine yol açabilir.

Şöyle görünüyor:

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

Daha fazla JEP 325: İfadeleri Değiştir (Önizleme)


2

Bunu Vavr kütüphanesini kullanarak halletmek mümkündür

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

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

Tüm vakaların hala açıkça listelenmesi gerektiğinden, bu elbette sadece küçük bir gelişmedir. Ancak özel yüklemi tanımlamak kolaydır:

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 bir ifadedir, bu nedenle burada Runnableyöntemleri doğrudan çağırmak yerine örnek gibi bir şey döndürür . Maç gerçekleştirildikten sonra Runnablegerçekleştirilebilir.

Daha fazla ayrıntı için lütfen resmi belgelere bakın .


1

alternatif olarak aşağıdaki gibi kullanabilirsiniz:

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

    }

veya aşağıdaki kod da çalışır

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

1

JEP 354: JDK-13 ve JEP 361'de Anahtar İfadeleri (Önizleme) : JDK-14'teki Anahtar İfadeleri (Standart) ,bir ifade olarak kullanılabilmesiiçin anahtar ifadesini genişletir.

Şimdi yapabilirsin:

  • doğrudan anahtar ifadesinden değişken atayın ,
  • yeni bir anahtar etiketi biçimi kullanın ( case L ->):

    Bir "durum L ->" anahtar etiketinin sağındaki kod bir ifade, blok veya (kolaylık olması açısından) bir fırlatma ifadesi ile sınırlıdır.

  • her durumda virgülle ayrılmış birden çok sabit kullanın
  • ve ayrıca artık değer sonu yok :

    Bir switch ifadesinden bir değer elde etmek için, breakwith value ifadesi bir yieldifade lehine bırakılır .

İfade değiştirme örneği:

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

Sabit kodlanmış değerleri kullanmak yerine bir alternatif, bunun yerine switch deyiminde aralık eşlemeleri kullanmak olabilir:

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.