String switch ifadesi neden boş bir durumu desteklemiyor?


125

Merak ediyorum neden Java 7 switchifadesi bir nullvakayı desteklemiyor ve bunun yerine atıyor NullPointerException? Aşağıdaki yorum satırına bakın (örnek Java Tutorials makalesindenswitch alınmıştır ):

{
    String month = null;
    switch (month) {
        case "january":
            monthNumber = 1;
            break;
        case "february":
            monthNumber = 2;
            break;
        case "march":
            monthNumber = 3;
            break;
        //case null:
        default: 
            monthNumber = 0;
            break;
    }

    return monthNumber;
}

Bu, ifher switchkullanımdan önce boş kontrol koşulunu ortadan kaldırırdı .


12
Dili yapan insanlar biz olmadığımız için buna kesin bir cevap yok. Tüm cevaplar saf varsayım olacaktır.
asteri

2
Açma girişimi nullbir istisnaya neden olacaktır. İçin bir ifkontrol yapın null, ardından switchifadeye girin.
gparyani

28
Gönderen JLS : Java programlama dilinin tasarımcıları kararda, [bir atma NullPointerExceptioniçin ifade değerlendirir eğer nullsessizce tüm switch deyimi atlanması veya sonrasında (eğer varsa) ifade işlemeye seçerek daha iyi bir sonuçtur çalışma zamanı at] varsayılan etiket (varsa).
gparyani

3
@gparyani: Bunu bir cevap haline getirin. Kulağa çok resmi ve kesin geliyor.
Thilo

7
@JeffGohlke: "Kararı veren kişi siz değilseniz neden sorusuna cevap vermenin bir yolu yok." ... gparyani'nin yorumu aksini kanıtlıyor
user541686

Yanıtlar:


145

Damryfbfnetsi olarak dışarı noktalarında yorumlarda, JLS §14.11 şu notu vardır:

nullAnahtar etiketi olarak kullanım yasağı, birinin asla yürütülemeyen kod yazmasını engeller. Eğer switchekspresyon bir referans tipte, bir Stringveya bir kutulu basit tür ya da enum türü, daha sonra, bir çalıştırma hatası sentezleme değerlendirilirse anlaşılacaktır nullzamanında. Java programlama dili tasarımcılarının kanaatine göre, bu, tüm switchifadeyi sessizce atlamaktan veya defaultetiketten (varsa ) sonra ifadeleri (varsa) yürütmeyi seçmekten daha iyi bir sonuçtur .

(vurgu benim)

Son cümle, kullanma olasılığını atlarken case null:, mantıklı görünüyor ve dil tasarımcılarının niyetlerine bir bakış sunuyor.

Uygulama ayrıntılarına bakmayı tercih edersek, Christian Hujer tarafından yazılan bu blog gönderisindenull , anahtarlarda neden izin verilmediğine dair bazı içgörülü spekülasyonlar var (her ne kadar enumanahtar yerine anahtarda ortalanmış olsa da String):

Başlık altında, switchifade tipik olarak bir tablesswitch bayt koduna derlenir. Ve "fiziksel" argüman switchve davaları ints. Açılacak int değeri, yöntemi çağırarak belirlenir Enum.ordinal(). [...] sıra sayıları sıfırdan başlar.

Yani, haritalama nulliçin 0iyi bir fikir olmaz. İlk enum değerindeki bir anahtar, null'dan ayırt edilemez. Belki de numaralandırmalar için sıra sayılarını 1'de saymaya başlamak iyi bir fikir olabilirdi. Ancak bu şekilde tanımlanmadı ve bu tanım değiştirilemez.

İken Stringanahtarları farklı uygulanır , enumdüğme ilk gelen ve referans olduğunda davranması gereken bir referans türüne geçiş nasıl emsal teşkil null.


1
Yalnızca uygulanmış olsaydı, a'nın bir parçası olarak boş işlemeye izin vermek büyük bir iyileştirme case null:olurdu String. Şu anda tüm Stringkontroller, eğer düzeltmek istiyorsak, yine de bir null-kontrol gerektirir, ancak çoğunlukla örtük olarak dizge sabitini olduğu gibi önce koyarak "123test".equals(value). Şimdi switch if (value != null) switch (value) {...
YoYo

1
"" Null değerini 0 ile yeniden eşlemek iyi bir fikir olmaz ":" ".hashcode () değeri 0 olduğundan bu yetersiz bir ifade! Bu, bir boş dizge ve sıfır uzunluklu bir dizenin bir switch deyiminde aynı şekilde ele alınması gerektiği anlamına gelir ki bu açıkça geçerli değildir.
skomisa

Enum durumunda, onları null değerini -1 ile eşlemekten alıkoyan şey neydi?
krispy

31

Genel nullolarak başa çıkmak kötüdür; belki daha iyi bir dil onsuz yaşayabilir null.

Sorununuz şu şekilde çözülebilir:

    switch(month==null?"":month)
    {
        ...
        //case "":
        default: 
            monthNumber = 0;

    }

monthBoş bir dizge ise iyi bir fikir değil : bu, boş dizge ile aynı şekilde ele alınacaktır.
gparyani

13
Çoğu durumda null değerini boş dizge olarak değerlendirmek tamamen mantıklı olabilir
Eric Woodruff

23

Güzel değil, ancak String.valueOf()bir anahtarda boş bir String kullanmanıza izin veriyor. Bulursa null, onu dönüştürür "null", aksi takdirde sadece geçirdiğiniz aynı String'i döndürür. "null"Açıkça idare etmezseniz , o zaman gider default. Tek uyarı, String "null"ile gerçek bir nulldeğişken arasında ayrım yapmanın bir yolu olmamasıdır .

    String month = null;
    switch (String.valueOf(month)) {
        case "january":
            monthNumber = 1;
            break;
        case "february":
            monthNumber = 2;
            break;
        case "march":
            monthNumber = 3;
            break;
        case "null":
            monthNumber = -1;
            break;
        default: 
            monthNumber = 0;
            break;
    }
    return monthNumber;

2
Java'da böyle bir şey yapmanın bir anti-model olduğuna inanıyorum.
Łukasz Rzeszotarski

2
@ ŁukaszRzeszotarski "Güzel değil" derken kastettiğim buydu
Krispy

15

Bu, neden fırlattığını cevaplama girişimidir. NullPointerException

Aşağıdaki javap komutunun çıktısı case, switchargüman dizesinin karma koduna göre seçildiğini ve dolayısıyla .hashCode()boş dizede çağrıldığında NPE attığını ortaya koymaktadır .

6: invokevirtual #18                 // Method java/lang/String.hashCode:()I
9: lookupswitch  { // 3
    -1826660246: 44
     -263893086: 56
      103666243: 68
        default: 95
   }

Bu, Java'nın hashCode'u farklı dizeler için aynı değeri üretebilir mi? , nadir de olsa, yine de eşleştirme olasılığı vardır (aynı hash koduna sahip iki dizge) Aşağıdaki örneğe bakın

    int monthNumber;
    String month = args[0];

    switch (month) {
    case "Ea":
        monthNumber = 1;
        break;
    case "FB":
        monthNumber = 2;
        break;
    // case null:
    default:
        monthNumber = 0;
        break;
    }
    System.out.println(monthNumber);

javap bunun için

  10: lookupswitch  { // 1
              2236: 28
           default: 59
      }
  28: aload_3       
  29: ldc           #22                 // String Ea
  31: invokevirtual #24                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  34: ifne          49
  37: aload_3       
  38: ldc           #28                 // String FB
  40: invokevirtual #24                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
  43: ifne          54
  46: goto          59 //Default

Gördüğünüz gibi , her bir vaka dizesi ile eşleşmeyi kontrol etmek için iki koşul için "Ea"ve "FB"ancak iki ifkoşulla üretilir . Bu işlevselliği uygulamanın çok ilginç ve karmaşık yolu!


5
Yine de bu başka bir şekilde uygulanabilirdi.
Thilo

2
Bunun bir tasarım hatası olduğunu söyleyebilirim.
Deer Hunter

6
Bunun, dize hash fonksiyonunun bazı şüpheli yönlerinin neden değiştirilmediğiyle bağlantılı olup olmadığını merak ediyorum: genel olarak kod hashCode, bir programın farklı çalıştırmalarında aynı değerleri döndürmeye dayanmamalı, ancak string hash'leri çalıştırılabilir dosyalara dönüştürüldüğünden derleyici, dize hash yöntemi dil spesifikasyonunun bir parçası olarak sona erer.
supercat

5

Uzun lafın kısası ... (ve umarım yeterince ilginç !!!)

Enum ilk olarak Java1.5'te ( Eylül'2004 ) tanıtıldı ve String'i açmaya izin vermeyi talep eden hata uzun süre önce dosyalandı ( Ekim'95 ). Jun'2004'te bu hatayla ilgili yayınlanan yoruma bakarsanız, bu hatayı erteledikleri ( görmezden geldikleri ) ve sonunda Java 1.5'i başlattıkları aynı yıl içinde 0'dan başlayan sıralı 'enum' ile başlattıkları ve ( cevapsız ) numaralandırma için boş değeri desteklememek. Daha sonra Java1.7'de ( Temmuz'2011 ) takip ettiler (Don't hold your breath. Nothing resembling this is in our plans. zorunlu) String ile aynı felsefe (yani bayt kodu üretilirken, hashcode () yöntemi çağrılmadan önce boş bir denetim yapılmadı).

Bu yüzden, enum'un ilk geldiği ve anahtar bloğundaki boş değeri destekleyemedikleri için 0'da ordinal başlangıcıyla uygulandığı ve daha sonra String ile aynı felsefeyi zorlamaya karar verdiler, yani boş değeri değil anahtar bloğunda izin verilir.

TL; DR String ile java kodunu bayt kod dönüşümüne uygularken NPE'yi (null için karma kod üretme girişiminden kaynaklanan) halledebilirlerdi, ancak sonunda yapmamaya karar verdiler.

Referans: TheBUG , JavaVersionHistory , JavaCodeToByteCode , SO


1

Java Belgelerine göre:

Anahtar, bayt, kısa, karakter ve ilkel veri türleriyle çalışır. Ayrıca, numaralandırılmış türler (Enum Türleri'nde tartışılmıştır), String sınıfı ve belirli ilkel türleri saran birkaç özel sınıfla çalışır: Karakter, Bayt, Kısa ve Tam Sayı (Sayılar ve Dizeler'de tartışılmıştır).

Yana nulltürü yok ve hiçbir şeyden bir örnek değil, bir switch deyimi ile çalışmaz.


4
Ve yine nullbir için geçerli bir değer olduğunu String, Character, Byte, Short, veya Integerreferans.
asteri

0

Cevap basitçe, referans tipli (kutulu bir ilkel tip gibi) bir anahtar kullanırsanız, ifade boş ise çalışma zamanı hatası ortaya çıkar çünkü kutudan çıkarmak NPE'yi atar.

bu nedenle case null (yasa dışıdır) yine de asla yürütülemez;)


1
Yine de bu başka bir şekilde uygulanabilirdi.
Thilo

OK @Thilo, bu uygulamaya benden daha akıllı insanlar dahil oldu. Bunun uygulanabileceği başka yollar biliyorsanız, bunların ne olduğunu bilmek isterim [ve eminim başkaları da vardır] bu yüzden paylaşın ...
amrith

3
Dizeler, kutuya alınmış ilkel türler değildir ve NPE, birisi onları "kutudan çıkarmaya" çalıştığı için oluşmaz.
Thilo

@thilo, bunu uygulamanın diğer yolları nelerdir?
2013

3
if (x == null) { // the case: null part }
Thilo

0

Https://stackoverflow.com/a/18263594/1053496 adresindeki anlayışlı yorumlara (başlık altında ....) katılıyorum@Paul Bellora'nın cevabında katılıyorum.

Deneyimlerimden bir neden daha buldum.

Eğer 'durum' null olabilir, bu da switch (değişken) 'in boş olduğu anlamına gelir, o zaman geliştirici eşleşen bir' boş 'durum sağladığı sürece sorun olmadığını söyleyebiliriz. Ancak, geliştirici eşleşen herhangi bir 'boş' durum sağlamazsa ne olur? O zaman bunu bir 'varsayılan' durumla eşleştirmemiz gerekir ki bu, geliştiricinin varsayılan durumda ele almayı amaçladığı şey olmayabilir. Bu nedenle 'boş' ile bir varsayılan eşleştirme 'şaşırtıcı davranışa' neden olabilir. Bu nedenle, 'NPE'nin atılması, geliştiricinin her durumu açıkça ele almasını sağlayacaktır. Bu durumda NPE atmayı çok düşünceli buldum.


0

Apache StringUtils sınıfını kullanın

String month = null;
switch (StringUtils.trimToEmpty(month)) {
    case "xyz":
        monthNumber=1;  
    break;
    default:
       monthNumber=0;
    break;
}
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.