Tamsayıyı int ile karşılaştırmak Java'da neden NullPointerException oluşturabilir?


81

Bu durumu gözlemlemek benim için çok kafa karıştırıcıydı:

Integer i = null;
String str = null;

if (i == null) {   //Nothing happens
   ...                  
}
if (str == null) { //Nothing happens

}

if (i == 0) {  //NullPointerException
   ...
}
if (str == "0") { //Nothing happens
   ...
}

Bu yüzden, ilk önce boks işleminin çalıştırıldığını (yani, java'nın int değerini çıkarmaya çalıştığını null) ve karşılaştırma işleminin daha düşük önceliğe sahip olduğunu düşündüğüm için istisna atıldı.

Soru şu: Java'da neden bu şekilde uygulanıyor? Neden boks, referansları karşılaştırmaktan daha yüksek önceliğe sahip? Ya da neden nullboks öncesinde doğrulama uygulamadılar ?

Şu anda NullPointerException, sarmalanmış ilkellerle atıldığında ve gerçek nesne türleriyle atıldığında tutarsız görünüyor .


Str.equals ("0") yaparsanız bir NullPointerException alırsınız.
Ash Burlaczenko

== operatörü bir zamanlar her koşulda NPE'lere karşı tasarruf sağlıyordu. Benim için bu, Java'da otomatik boksu başlatmanın ne kadar kötü bir fikir olduğunu gösteren başka bir örnek. Pek çok nedenden ötürü uymuyor ve daha önce orada olmayan hiçbir şey sunmuyor. Yalnızca, gerçekte neler olup bittiğini gizlerken kodu daha kısa kod yapar.
x4u

Düşüncelerim 180 derece farklı. Nesneleri her yerde kullanan ilkelleri dahil etmemeleri gerekirdi. Sonra derleyicinin ilkelleri optimize etmesine ve kullanmasına izin verin. O zaman herhangi bir karışıklık olmazdı.
MrJacqes

Yanıtlar:


138

Kısa Cevap

Kilit nokta şudur:

  • == iki referans türü arasında her zaman referans karşılaştırmasıdır
    • Çoğu zaman, örneğin Integerve ile String, equalsbunun yerine kullanmak istersiniz
  • == referans türü ile sayısal ilkel tür arasında her zaman sayısal karşılaştırmadır
    • Referans türü, kutudan çıkarma dönüşümüne tabi tutulacaktır
    • Kutudan çıkarma nullher zaman fırlatırNullPointerException
  • Java'nın birçok özel tedavisi Stringolsa da, aslında ilkel bir tür DEĞİLDİR

Yukarıdaki ifadeler, herhangi bir geçerli Java kodu için geçerlidir . Bu anlayışla, sunduğunuz pasajda herhangi bir tutarsızlık yoktur.


Uzun Cevap

İlgili JLS bölümleri şunlardır:

JLS 15.21.3 Referans Eşitlik Operatörleri ==ve!=

Bir eşitlik operatörünün işlenenlerinin her ikisi de referans türünde veya boş türdeyse , işlem nesne eşitliğidir.

Bu, aşağıdakileri açıklar:

Integer i = null;
String str = null;

if (i == null) {   // Nothing happens
}
if (str == null) { // Nothing happens
}
if (str == "0") {  // Nothing happens
}

Her iki işlenen de referans türlerdir ve bu nedenle ==referans eşitlik karşılaştırmasıdır.

Bu aynı zamanda aşağıdakileri de açıklar:

System.out.println(new Integer(0) == new Integer(0)); // "false"
System.out.println("X" == "x".toUpperCase()); // "false"

İçin ==sayısal eşitliğin olması, terimin en az bir sayısal bir türü olmalıdır :

JLS 15.21.1 Sayısal Eşitlik Operatörleri ==ve!=

Bir eşitlik operatörünün işlenenlerinin her ikisi de sayısal türdeyse veya biri sayısal türdeyse ve diğeri sayısal türe dönüştürülebilirse , işlenenler üzerinde ikili sayısal yükseltme gerçekleştirilir. İşlenenlerin yükseltilmiş türü intveya ise long, o zaman bir tamsayı eşitlik testi gerçekleştirilir; terfi türü float or double` ise, kayan noktalı eşitlik testi gerçekleştirilir.

İkili sayısal yükseltmenin, değer kümesi dönüştürme ve kutudan çıkarma dönüştürme gerçekleştirdiğini unutmayın.

Bu açıklıyor:

Integer i = null;

if (i == 0) {  //NullPointerException
}

İşte Etkili Java 2. Baskı, Öğe 49'dan bir alıntı : İlkelleri kutulu ilkellere tercih et :

Özetle, seçiminiz olduğunda kutulu ilkel yerine ilkelleri kullanın. İlkel türler daha basit ve daha hızlıdır. Kutulu ilkelleri kullanmanız gerekiyorsa, dikkatli olun! Otomatik kutulama, kutulu temelleri kullanmanın ayrıntı düzeyini azaltır ancak tehlikeyi azaltmaz. Programınız iki kutulu ilkeli ==operatörle karşılaştırdığında, bir kimlik karşılaştırması yapar, ki bu neredeyse kesinlikle istediğiniz şey değildir. Programınız kutulu ve kutulu olmayan ilkelleri içeren karma tip hesaplamalar yaptığında, kutudan çıkarma yapar ve programınız kutudan çıkarma yaptığında fırlatabilir NullPointerException. Son olarak, programınız ilkel değerleri kutuya aldığında, maliyetli ve gereksiz nesne yaratımlarına neden olabilir.

Kutulu ilkelleri kullanmaktan başka seçeneğiniz olmayan yerler vardır, örneğin jenerikler, ancak aksi takdirde kutulu ilkelleri kullanma kararının haklı olup olmadığını ciddi olarak düşünmelisiniz.

Referanslar

İlgili sorular

İlgili sorular


2
Neden someRef == 0 her zaman sayısal karşılaştırma olduğuna gelince , bu çok sağlam bir seçimdir, çünkü iki kutulu ilkellerin referanslarını karşılaştırmak neredeyse her zaman bir programcı hatasıdır. Bu durumda karşılaştırmalara atıfta bulunmak gereksiz olacaktır.
Mark Peters

3
Neden derleyici ifadeyi değiştirmek olmaz (myInteger == 0)ile (myInteger != null && myInteger == 0)yerine bu klişe boş denetimi kodu yazmak için geliştirici güvenmek? IMO Kontrol edebilmeliyim if (myBoolean)ve bu, trueancak ve ancak temel değerin spesifik olarak olup olmadığını değerlendirmelidir true- İlk önce boş kontrol yapmam gerekmemeli.
Josh M.

15

Otomatik kutulama sayesinde, NPE örneğiniz bu koda eşdeğerdir :

if ( i.intValue( ) == 0 )

Bu nedenle NPE halinde iolan null.


4
if (i == 0) {  //NullPointerException
   ...
}

i bir Tamsayıdır ve 0 bir tamsayıdır, dolayısıyla gerçekten yapılan şey böyle bir şeydir

i.intValue() == 0

Ve bu, nullPointer'a neden olur çünkü i null. String için bu işleme sahip değiliz, bu yüzden orada bir istisna yok.


4

Java'nın yapımcıları, ==operatörü farklı türlerdeki işlenenler üzerinde doğrudan hareket edecek şekilde tanımlamış olabilirler ; bu durumda Integer I; int i;, karşılaştırma verildiğinde , I==i;" Değeri olan bir değere Iatıfta bulunuyor mu?" Sorusu sorulabilir - zorluk çekmeden yanıtlanabilen bir soru boş olduğunda bile . Ne yazık ki Java, farklı türlerdeki işlenenlerin eşit olup olmadığını doğrudan kontrol etmez; bunun yerine, dilin herhangi bir işlenenin türünün diğerinin türüne dönüştürülmesine izin verip vermediğini kontrol eder ve - eğer öyleyse - dönüştürülen işleneni dönüştürülmemiş olanla karşılaştırır. Değişkenler için bu tür davranış araçları , ve tiplerinin bazı kombinasyonlar ile bu olması mümkündür ve ancakIntegeriIxyzx==yy==zx!=z[örneğin x = 16777216f y = 16777216 z = 16777217]. Bu aynı zamanda, karşılaştırmanın I==i"I'yi bir'e dönüştür intve bu bir istisna oluşturmazsa, ile karşılaştır " olarak çevrildiği anlamına gelir i.


+1: OP'nin "Bu neden böyle tasarlandı?" Sorusuna gerçekten cevap vermeye çalıştığınız için.
Martijn Courteaux

1
@MartijnCourteaux: Pek çok dil, yalnızca eşleşen türlerin işlenenleri için operatörleri tanımlıyor gibi görünüyor ve bir T'nin dolaylı olarak U'ya dönüştürülebilmesi durumunda, bu tür örtük dönüştürmenin bir U'nun kabul edilebildiği ancak bir T'nin kabul edemediği her zaman şikayet edilmeden gerçekleştirilmesi gerektiğini varsayıyor. Öyle değil böyle davranış için, bir dil tanımlayabiliriz mıydı ==eğer her durumda bu şekilde x==y, y==zve x==zşikayet olmadan tüm derleme, üç karşılaştırmalar bir denklik ilişkisi olarak davranacaktır. Tasarımcıların her türden süslü dil özelliğini zorlaması, ancak aksiyomatik uyumu görmezden gelmesi meraklı.
supercat

1

Bunun nedeni Javas'ın otomatik kutulama özelliği. Derleyici, karşılaştırmanın sağ tarafında ilkel bir tam sayı kullandığınızı ve sarmalayıcı Tamsayı değerini de ilkel bir int değerine açmanız gerektiğini algılar.

Bu mümkün olmadığından (sıraladığınız gibi geçersizdir) NullPointerExceptionatılır.


1

Gelen i == 0Java (yani, "tarafından başvurulan sargı nesnesinde kayıtlı değer otomatik kutudan çıkarma yapmaya denemek ve sayısal bir karşılaştırma yapacak ideğeri ile aynı 0?").

Yana iolan nullkutudan çıkarma bir atacağım NullPointerException.

Mantık şu şekildedir:

JLS § 15.21.1 Sayısal Eşitlik Operatörlerinin ilk cümlesi == ve! = Şu şekildedir:

Bir eşitlik operatörünün işlenenlerinin her ikisi de sayısal türdeyse veya biri sayısal türdeyse ve diğeri sayısal türe dönüştürülebilirse (§5.1.8), işlenenler üzerinde ikili sayısal yükseltme gerçekleştirilir (§5.6.2).

Açıkça ibir sayısal türe dönüştürülebilir ve 0sayısal bir türdür, bu nedenle ikili sayısal yükseltme işlenenler üzerinde gerçekleştirilir.

§ 5.6.2 İkili Sayısal Promosyon diyor ki (diğer şeylerin yanı sıra):

İşlenenlerden herhangi biri referans tipindeyse, kutudan çıkarma dönüştürme (§5.1.8) gerçekleştirilir.

§ 5.1.8 Unboxing Conversion diyor (diğer şeylerin yanı sıra):

Eğer R null, kutudan çıkarma dönüşüm a atarNullPointerException


0

Basitçe bir yöntem yazın ve NullPointerException'dan kaçınmak için çağırın.

public static Integer getNotNullIntValue(Integer value)
{
    if(value!=null)
    {
        return value;
    }
    return 0;
}
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.