Java'da boşları işlemenin en iyi yolu? [kapalı]


21

NullPointerException nedeniyle başarısız olan bazı kodlar var. Nesnenin olmadığı nesnede bir yöntem çağrılıyor.

Ancak bu, bunu düzeltmenin en iyi yolu hakkında düşünmeme neden oldu. Boş göstergeleri istisnalar için ileride ispat kodunu kullanmak için her zaman boş kodları kodluyor muyum, yoksa boşluğun nedenini aşağı doğru oluşmayacak şekilde düzeltmeliyim.

Düşüncelerin nelerdir?


5
Neden "boşun nedenini düzeltmedin"? Bunun tek mantıklı seçenek olmadığını gösteren bir örnek verebilir misiniz? Açıkçası, bir şey sizi barizden uzaklaştırıyor olmalı. Bu ne? Neden root'unuzu düzeltmek ilk tercihinize neden gelmiyor?
S.Lott

Sorunun "kök nedenini" iyi bir şekilde çözmek kısa vadede yardımcı olabilir, ancak kod hala nullları savunmadan kontrol etmiyorsa ve null tanıtabilecek başka bir işlem tarafından yeniden kullanılıyorsa, tekrar düzeltmem gerekir.
Shaun F

2
@Shaun F: Kod bozuksa, bozulur. Kodun beklenmedik null üretmesi ve düzeltilmemesi nasıl mümkün değil. Açıkçası, bazı "aptal yönetimi" ya da "siyasi" şeyler oluyor. Ya da hatalı kodun bir şekilde kabul edilebilir olmasına izin veren bir şey. Hatalı kodun nasıl çözülmediğini açıklayabilir misiniz? Bu soruyu sormana neden olan politik arka plan nedir?
S.Lott

1
"içinde boş olan daha sonra veri yaratmaya açık" Bu ne anlama geliyor? "Veri oluşturma yordamını düzeltebilirim" ise, başka bir güvenlik açığı yoktur. Bunun "içinde boş olan daha sonra veri yaratmanın" ne anlama gelebileceğini anlayamıyorum. Buggy yazılımı?
S.Lott

1
@ S.Lott, kapsülleme ve tek sorumluluk. Tüm kodunuz diğer tüm kodların ne yaptığını "bilmiyor". Bir sınıf Froobinator'ü kabul ederse, Froobinator'ü kimin ve nasıl yarattığı hakkında mümkün olduğunca az varsayım yapar. "Harici" koddan geliyor veya kod tabanının farklı bir kısmından geliyor. Buggy kodunu düzeltmemelisin demiyorum; fakat "savunma programlaması" nı arayın ve OP'nin neyi kastettiğini bulacaksınız.
Paul Draper,

Yanıtlar:


45

Null, yönteminiz için makul bir giriş parametresiyse, yöntemi düzeltin. Değilse, arayanı onarın. "Makul" esnek bir terimdir, bu nedenle aşağıdaki testi öneriyorum: Yöntem boş bir girişi nasıl vermeli? Birden fazla olası cevap bulursanız, null makul bir girdi değildir .


1
Gerçekten bu kadar basit.
biziclop

3
Guava, null kontrolünü basitleştiren çok güzel yardımcı metotlara sahiptir Precondition.checkNotNull(...). Stackoverflow.com/questions/3022319/…

Benim kuralım, mümkünse her şeyi mantıklı varsayılanlara sıfırlamak ve sonradan null istisnalar hakkında endişelenmektir.
davidk01

Thorbjørn: Yani, yanlışlıkla NullPointerException'ı kasıtlı bir NullPointerException ile değiştirir mi? Bazı durumlarda, erken kontrol yapılması faydalı veya hatta gerekli olabilir; fakat çok az değer katan ve programı daha da büyüten anlamsız kazan plakaları boşuna korkuyorum.
user281377

6
Null, yönteminize uygun bir girdi parametresi değilse, ancak yöntemin çağrıları yanlışlıkla boşa geçme olasılığı varsa (özellikle yöntem genelse), yöntemin IllegalArgumentExceptionboş bırakıldığında bir yöntem atmasını da isteyebilirsiniz . Bu, yöntemin arayanlara, hatanın kodunda (yöntemin kendisinde değil) olduğunu gösterir.
Brian

20

Boş kullanmayın, isteğe bağlı kullanın

Belirttiğiniz gibi null, Java'daki en büyük sorunlardan biri, her yerde veya en azından tüm referans türleri için kullanılabilmesidir.

Bunun olabileceğini nullve olamayacağını söylemek imkansız .

Java 8 tanıtır çok daha iyi bir model: Optional.

Ve Oracle'dan örnek:

String version = "UNKNOWN";
if(computer != null) {
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null) {
    USB usb = soundcard.getUSB();
    if(usb != null) {
      version = usb.getVersion();
    }
  }
}

Bunların her biri başarılı bir değer verebilir veya getiremezse, API’leri Optionals olarak değiştirebilirsiniz :

String name = computer.flatMap(Computer::getSoundcard)
    .flatMap(Soundcard::getUSB)
    .map(USB::getVersion)
    .orElse("UNKNOWN");

Türdeki seçeneği açıkça kodlayarak, arabirimleriniz çok daha iyi olacak ve kodunuz daha temiz olacaktır.

Java 8 kullanmıyorsanız com.google.common.base.Optional, Google Guava’ya bakabilirsiniz .

Guava ekibi tarafından iyi bir açıklama: https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained

Bazı dillerden örneklerle null dezavantajlarının daha genel bir açıklaması: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/


@Nonnull, @Nullable

Java 8, IDE'ler gibi kod kontrol araçlarının sorunlara yakalanmasına yardımcı olmak için bu ek açıklamaları ekler. Etkinlikleri konusunda oldukça sınırlıdırlar.


Ne zaman anlamlı olduğunu kontrol et

Kodunuzun% 50'sini boş (null) kontrol etmeyin, özellikle kodunuzun bir nulldeğerle yapabileceği makul bir şey yoksa .

Öte yandan, eğer nullkullanılabiliyorsa ve bir şey ifade ediyorsa , onu kullandığınızdan emin olun.


Sonuçta, belli ki nullJava'dan kaldıramazsınız . OptionalMümkün olduğunca soyutlamayı değiştirmenizi ve nullbu konuda makul bir şeyler yapabileceğiniz diğer zamanları kontrol etmenizi şiddetle tavsiye ederim .


2
Normalde, dört yıllık soruların cevapları düşük kalite nedeniyle silinir. Java 8'in (o zamanlar mevcut olmayan) sorunu nasıl çözebileceğinden bahsetmek iyi bir iş.

fakat tabii ki asıl soru ile alakasız. Ve ne söylenebilir ki argüman gerçekten isteğe bağlıdır?
15’te

5
@jwenting Orijinal soru ile tamamen ilgili. "Bir tornavidayla çiviyi sürmenin en iyi yolu nedir" cevabı "Tornavida yerine çekiç kullanın" dır.
Daenyth, 04

@jwenting, sanırım bunu anladım ama netlik için: argüman isteğe bağlı değil, genelde null değerini kontrol etmezdim. 100 satır boş kontrol ve 40 satır madde olacak. Daha genel arayüzlerde veya null gerçekten mantıklı olduğunda null olup olmadığını kontrol edin (yani, argüman isteğe bağlıdır).
Paul Draper

4
J-Boss @, nasıl, Java, olur değil bir kontrolsüz olması NullPointerException? Java’da bir örnek yöntemi her çağırdığınızda, NullPointerExceptionkelimenin tam anlamıyla A olabilir . Şimdiye kadar neredeyse her yöntemde olurdu . throws NullPointerException
Paul Draper

8

Bunun üstesinden gelmenin birçok yolu vardır, kodunuzu biberle doldurmak if (obj != null) {}ideal değildir, dağınıktır, bakım döngüsü sırasında kodu okurken gürültü ekler ve bu kazan plakası sarımını yapmayı unutmak kolay olduğu için hataya açıktır.

Kodun sessizce çalışmaya devam edip etmemesini isteyip istemediğinize bağlıdır. nullBir hata mı yoksa beklenen bir durum mu.

Null nedir

Her tanım ve vakada, Null , mutlak veri eksikliğini temsil eder. Veritabanlarındaki boş değerler, bu sütun için bir değer bulunmadığını gösterir. boş Stringboş aynı değildir Stringbir null intteoride, ZERO ile aynı şey değildir. Uygulamada "bağlıdır". Boş , iş mantığına bağlı olduğu için String sınıfı için Stringiyi bir Null Objectuygulama yapabilir Integer.

Alternatifler:

  1. Null ObjectDesen. Durumu temsil eden nesnenizin bir örneğini oluşturun nullve bu türe yapılan tüm başvuruları uygulamaya referans alarak başlatın Null. Bu, geçerli nullolabilecek nullbir durum olabilir ve beklenen diğer nesnelere çok fazla referansı olmayan basit değer tipi nesneler için kullanışlıdır .

  2. Null CheckerParametrelerin boş kalmasını önleyen bir yönü olan yöntemleri dokumak için Aspect Oriented araçlarını kullanın . Bu nullbir hatanın olduğu durumlar içindir .

  3. Gürültüden assert()daha iyi değil, daha az kullanın if (obj != null){}.

  4. Java Sözleşmeleri gibi bir Sözleşme Uygulama aracı kullanın . AspectJ gibi bir durumla aynı ancak daha yeni olan ve harici yapılandırma dosyaları yerine Ek Açıklamalar kullanan durum. Yönleri ve Varlıkların her iki eserinin de en iyisi.

1, gelen verilerin bilindiği nullve bazı varsayılan değerlerle değiştirilmesi gerektiğine göre ideal bir çözümdür, böylece memba tüketicileri tüm boş kontrol kazan plakası koduyla uğraşmak zorunda kalmayacaklardır. Bilinen varsayılan değerlere karşı kontrol etmek de daha anlamlı olacaktır.

2, 3 ve 4 NullPointerException, her zaman ve iyileştirme olan daha bilgilendirici bir şeyle değiştirmek için alternatif istisna jeneratörleridir .

Sonunda

nullJava’da neredeyse her durumda bir mantık hatası var. Kök nedenini ortadan kaldırmak için her zaman çaba sarf etmeniz gerekir NullPointerExceptions. nullKoşulları iş mantığı olarak kullanmamaya çalışmalısınız . if (x == null) { i = someDefault; }sadece bu varsayılan nesne örneğine ilk atamayı yap.


Eğer mümkünse null nesne kalıbı bir yoldur, bir null durumu ele almanın en zarif yoludur. Üretimde bir şeyler patlatmak için boş bir numara istemeniz nadirdir!
Richard Miskin,

@Richard: nullBeklenmedik olmadıkça gerçek bir hatadır, o zaman her şey tamamen durur.

Veritabanlarında ve programlama kodlarında boş değer önemli bir açıdan farklı şekilde ele alınır: Programlamada null == null(PHP'de bile), ancak Veritabanlarında, null != nullçünkü veritabanlarında "hiçbir şey" yerine , bilinmeyen bir değeri temsil eder . İki bildiri eşit değil, iki bildiri eşit değil.
Simon Forsberg,

8

Boş kontroller eklemek testi problemli hale getirebilir. Bu harika dersi görün.

Google Tech görüşmeleri konferansına bakın: "Temiz Kod Konuşmaları - Şeylere Bakmayın!" 24 dakika hakkında konuşuyor

http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E

Paranoyak programlama, her yere boş kontroller eklemeyi içerir. İlk bakışta iyi bir fikir gibi gözükse de, test açısından bakıldığında, boş kontrol tipi testinizi yapmayı zorlaştırıyor.

Ayrıca, gibi bazı nesnelerin varlığı için bir ön koşul oluşturduğunuzda

class House(Door door){

    .. null check here and throw exception if Door is NULL
    this.door = door
}

bir istisna atacağınız için Evi yaratmanızı önler. Test durumlarınızın Kapı dışında bir şeyi test etmek için sahte nesneler oluşturduğunu varsayın, bunu yapamazsınız çünkü kapı gereklidir

Sahte yaratma cehennemi ile acı çekenler bu tür sıkıntıların farkındadır.

Özetle, test takımınız Kapılar, Evler, Çatılar veya herhangi bir şekilde paranoyak olması gerekmeden test etmek için yeterince sağlam olmalıdır. Seroiusly, testinizde belirli nesneler için boş bir kontrol testi eklemek ne kadar zor :)

Her zaman işe yarayan uygulamaları tercih etmelisiniz, çünkü çalışır durumda olan HOPING yerine çalışır durumda olduğunu kanıtlayan birkaç testiniz vardır, çünkü her yerde bir sürü önkoşul null kontrolü var.


4

tl; dr - beklenmedik bir durum olup olmadığını kontrol etmek İYİDİR, nullancak bir uygulamanın bunları iyi hale getirmeye çalışması için KÖTÜ.

ayrıntılar

Açıkçası, nullbir yönteme geçerli bir giriş veya çıktının, diğerlerinin olmadığı durumlarda var.

Kural 1:

Bir sağlayan bir yöntem için javadoc nullparametresini veya döndüren nulldeğeri olmalıdır açıkça belgelemek ve ne anlatmak nulldemektir.

Kural 2:

Bir API yöntemi, nullbunu yapmak için iyi bir neden olmadıkça kabul veya iade olarak belirtilmemelidir .

Bir yöntemin “sözleşmesinin” açıkça belirtilmesi durumunda null, vermemeniz gereken bir yerden geçmek veya geri dönmek bir programlama hatasıdırnull .

Kural # 3:

Bir uygulama programlama hatalarını "iyi yapmayı" denememelidir.

Bir yöntem nullorada olmaması gerektiğini tespit ederse , sorunu başka bir şeye dönüştürerek düzeltmeyi denememelidir. Bu sadece problemi programlayıcıdan gizler. Bunun yerine, NPE'nin olmasına izin vermeli ve bir hataya neden olmalıdır, böylece programcı kök nedenin ne olduğunu bulabilir ve düzeltebilir. Umarım test sırasında başarısızlık fark edilir. Değilse, test metodolojiniz hakkında bir şeyler söylüyor.

Kural 4:

Mümkün olan durumlarda programlama hatalarını erken tespit etmek için kodunuzu yazın.

Kodunuzda çok sayıda NPE ile sonuçlanan hatalar varsa, en zor şey, nulldeğerlerin nereden geldiğini bulmak olabilir . Teşhisi kolaylaştırmanın bir yolu, kodunuzu nullen kısa sürede algılanacak şekilde yazmaktır . Genellikle bunu diğer çeklerle birlikte yapabilirsiniz; Örneğin

public setName(String name) {
    // This also detects `null` as an (intended) side-effect
    if (name.length() == 0) {
        throw new IllegalArgumentException("empty name");
    }
}

(3 yöneten ve 4 gerçeklik yoğrulması gerektiğini durumlar örneğin (kural 3) için, bazı uygulamaların çeşit. Açıkçası vardır gerekir muhtemelen tespit programlama hataları sonra çalışmaya devam ediyoruz. Ve (kötü parametreler için kural 4) çok fazla kontrol olabilir performans etkisi.)


3

Savunması için yöntemi tamir etmenizi tavsiye ederim. Örneğin:

String go(String s){  
    return s.toString();  
}

Bunun çizgisinde daha fazlası olmalı:

String go(String s){  
    if(s == null){  
       return "";  
    }     
    return s.toString();  
}

Bunun kesinlikle önemsiz olduğunun farkındayım, ancak istilacı bir cisimden cihaza boş bir neyin geçmesine neden olmayacak bir nesne vermesini beklerse.


10
Bunun, arayan kişinin (bu API'ye kodlayan programcı), "varsayılan" davranışı elde etmek için bir parametre olarak boş geçme alışkanlığı geliştirmesi nedeniyle, kötü bir yan etkisi vardır.
Goran Joviç

2
Eğer bu Java ise, hiç kullanmamalısınız new String().
biziclop

1
@biziclop aslında çoğu farkına varmaktan çok daha önemlidir.
Woot4Moo

1
Bence argüman null ise bir iddiaya girmek ve bir istisna atmak daha mantıklı, çünkü açıkça arayan kişinin hatası. Sessizce arayanların hatalarını "düzeltmeyin"; bunun yerine, onların farkında olmalarını sağlayın.
Andres F.

1
@ Woot4Moo O zaman bir istisna atan kendi kodunuzu yazın. Önemli olan, arayan kişiye geçtikleri argümanların yanlış olduğunu bildirmek ve mümkün olan en kısa sürede söylemektir. S'yi sessizce düzeltmek nullmümkün olan en kötü seçenektir, NPE atmaktan daha kötü.
Andres F.

2

Null ile ilgili aşağıdaki genel kurallar bana şu ana kadar çok yardımcı oldu:

  1. Veriler kontrolünüzün dışından geliyorsa, sistematik olarak boşları kontrol edin ve uygun şekilde hareket edin. Bu, işleve anlamlı gelen bir istisna atmak anlamına gelir (işaretli veya işaretsiz, istisna adının tam olarak ne olduğunu söylediğinden emin olun). Ancak, ASLA sisteminizde potansiyel olarak sürpriz taşıyabilecek bir değer bulunmasına izin vermeyin.

  2. Null, veri modeliniz için uygun değerler alanındaysa, uygun şekilde kullanın.

  3. Değerleri döndürürken, mümkün olduğunda boş değer döndürmemeye çalışın. Her zaman Boş listeleri, boş dizeleri, Boş Nesne kalıplarını tercih et. Belirli bir kullanım durumu için verilerin mümkün olan en iyi temsili olması durumunda null değerini döndürülen değerler olarak saklayın.

  4. Muhtemelen hepsinden önemlisi ... Testler, testler ve tekrar test. Kodunuzu test ederken kodlayıcı olarak test etmeyin, Nazi psikopat dominatrix olarak test edin ve bu kodun dışına işkence yapmanın her türlü yolunu hayal etmeye çalışın.

Bu, paranoyak tarafta, genellikle sistemleri dış dünyaya bağlayan cephelere ve proxy'lere neden olan boşluklara ve içte bol miktarda fazlalık ile sıkı bir şekilde kontrol edilen değerleri kontrol etmemize neden olur. Buradaki dış dünya, kendimi kodlamadığım her şey anlamına geliyor. Maliyet çalışma zamanı taşıyor, ancak şimdiye kadar nadiren kodun "boş güvenli bölümleri" oluşturarak bunu optimize etmek zorunda kaldım. Sağlık bakımı için çoğunlukla uzun süre çalışan sistemler oluşturduğumu söylemeliyim ve istediğim son şey, beklenmedik bir boş gösterici nedeniyle beklenmedik bir boş işaretçi nedeniyle çöktüğü için iyot alerjilerinizi CT tarayıcısına çarpıyor. kesme işaretleri veya 但 耒耨。 gibi karakterler içerebilir.

neyse .... benim 2 kuruş


1

İşlevsel dillerden Option / Some / None desenini kullanmayı öneriyorum. Bir Java uzmanı değilim, ancak C # projemde bu kalıbı kendi uygulamamı kullanıyorum ve Java dünyasına dönüştürülebileceğinden eminim.

Bu modelin arkasındaki fikir şudur: eğer bir değerin bulunmadığı durumlarda (örneğin, veri tabanından yeniden alındığında) mantıklı bir durum varsa, örneğin T'nin olası bir değer olduğu Seçenek [T] tipinde bir nesne sağlarsınız. . I Hiçbir değer [T] sınıfının bir değer nesnesinin olmaması durumunda, değeri içeren bazı [T] değerinin - nesnesinin varlığı durumunda döndürülür.

Bu durumda, değer yokluğu olasılığını ele almanız gerekir ve kod incelemesi yaparsanız kolayca yanlış kullanımın yerini görebilirsiniz. C # dili uygulamasından ilham almak için bitbucket havuzumun https://bitbucket.org/mikegirkin/optionsomenone adresine bakın.

Boş bir değer döndürürseniz ve mantıksal olarak bir hataya eşdeğerse (örneğin, dosya yok veya bağlanamıyor) bir istisna atmanız veya başka bir hata işleme modeli kullanmanız gerekir. Bunun arkasındaki fikir, yine, değer yokluğu durumunu ele almanız gerektiğinde ve kodda yanlış kullanım yerlerini kolayca bulabileceğiniz zaman çözümle bitiyorsunuz.


0

Metodunuzun olası sonuçlarından biri boş bir değer ise, bunun için defansif olarak kodlamanız gerekir, ancak eğer bir yöntemin boş olmayan bir değeri döndürmesi gerekiyorsa, ama kesin olarak düzeltmemeliyim.

Her zamanki gibi, hayattaki birçok şeyde olduğu gibi, bu duruma da bağlı. :)



0
  1. Gerçekten isteğe bağlı olmadıkça İsteğe bağlı türü kullanmayın, genellikle bir seçenek olarak boş bekliyorsanız ve düzenli olarak buggy kodunu yazdığınızdan, çıkış bir istisna olarak daha iyi ele alınır.

  2. Google'ın makalesinde de belirtildiği gibi, sorun kullandığı türden farklı değildir. Sorun, boş değerlerin kontrol edilmesi ve kullanılması gerektiği, sıklıkla dikkatlice çıktıklarıdır.

  3. Programın rutin işlemlerinin dışındaki alanlarda geçersiz koşulları gösteren çok sayıda boş durum vardır (geçersiz kullanıcı girişi, veritabanı sorunları, ağ arızası, eksik dosyalar, bozuk veriler). eğer sadece giriş yapmak için.

  4. İstisna işleme, JVM'de, İsteğe Bağlı gibi bir türün aksine, işleyicinin belleğe öncelikli tembel yüklemesi için erken destek de dahil olmak üzere, kendi oluşumlarının istisnai doğası için anlamlı olan bir türden farklı olarak, farklı operasyonel önceliklere ve ayrıcalıklara izin verilir. her zaman takılmaya ihtiyacım var.

  5. Her yerde, yalnızca oluşabilecekleri yerde, güvenilmez veri hizmetine erişme potansiyeli olan her yerde boş işleyicileri yazmanıza gerek yoktur ve bunların çoğu kolayca kolayca soyutlanabilecek birkaç genel kalıba girdiğinden Nadir durumlar dışında bir işleyici çağrısı.

Bu yüzden cevabım, kontrol edilen bir istisnaya göre sarılır ve ele alınır, ya da sizin yeteneğiniz dahilindeyse güvenilmez kodu düzeltin.

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.