Avoiding! = Null ifadeler


4014

object != nullÖnlemek için çok kullanıyorum NullPointerException.

Buna iyi bir alternatif var mı?

Örneğin sık sık kullanıyorum:

if (someobject != null) {
    someobject.doCalc();
}

Bu , yukarıdaki snippet'teki nesne için bir NullPointerExceptionolup olmadığını denetler someobject.

Kabul edilen cevabın güncel olmadığını unutmayın, daha yeni bir yaklaşım için bkz. Https://stackoverflow.com/a/2386013/12943 .


120
@Shervin Boş değerlerin teşvik edilmesi kodu daha az anlaşılır ve daha az güvenilir hale getirir.
Tom Hawtin - çakmak hattı

44
Elvis operatörleri önerildi, ancak Java 7'de olmayacak gibi görünüyor . Çok kötü, ?. ?: ve? [] inanılmaz zaman kazandırır.
Scott

72
Null kullanmamak, buradaki diğer önerilerin çoğundan daha üstündür. İstisnalar atın, geri dönmeyin veya boş değerlere izin vermeyin. BTW - 'assert' anahtar kelimesi varsayılan olarak devre dışı olduğu için işe yaramaz. Her zaman etkin bir arıza mekanizması kullanın
ianpojman

13
Şimdi Scala kullanmamın bir nedeni bu. Scala'da her şey geçersiz sayılmaz. "Hiçbir şey" iletilmesine veya geri dönmesine izin vermek istiyorsanız, yalnızca T als bağımsız değişkeni veya dönüş türü yerine Seçenek [T] 'yi kullanmanız gerekir.
Thekwasti

11
@thSoft Gerçekten, Scala'nın harika Optiontürü, dilin kontrol etme veya izin vermeme konusundaki isteksizliğiyle gölgelenir null. Ben hackernews üzerinde bu söz ve aşağı düştü ve "zaten Scala hiç kimse null kullanmaz" dedim. Bilin bakalım, iş arkadaşı tarafından yazılmış Scala'da zaten boş değerler buluyorum. Evet, "yanlış yapıyorlar" ve bu konuda eğitilmelidirler, ama gerçek şu ki dilin tip sistemi beni bundan korumalı ve etmiyor :(
Andres F.

Yanıtlar:


2642

Bu benim için orta ve orta seviyedeki geliştiricilerin bir noktada karşılaştıkları oldukça yaygın bir sorun gibi görünüyor: katıldıkları sözleşmeleri bilmiyorlar ya da güvenmiyorlar ve null'ları defalarca kontrol ediyorlar. Ek olarak, kendi kodlarını yazarken, arayanın boş değerleri kontrol etmesini gerektiren bir şeyi belirtmek için boş değer döndürme eğilimindedirler.

Bunu başka bir deyişle, null kontrolün ortaya çıktığı iki durum vardır:

  1. Null'un sözleşme açısından geçerli bir yanıt olduğu durumlarda; ve

  2. Geçerli bir yanıt olmadığı durumlarda.

(2) kolaydır. assertİfadeleri (iddialar) kullanın veya hataya izin verin (örneğin, NullPointerException ). İddialar 1.4'te eklenen oldukça az kullanılan bir Java özelliğidir. Sözdizimi:

assert <condition>

veya

assert <condition> : <object>

burada <condition>bir boole ifadesi ve yönteminin çıktısı hataya dahil edilecek <object>bir nesnedir toString().

Koşul doğru değilse bir assertifade bir Error( AssertionError) atar . Varsayılan olarak, Java iddiaları yoksayar. Seçeneği JVM'ye ileterek onaylamaları etkinleştirebilirsiniz -ea. Bireysel sınıflar ve paketler için onaylamaları etkinleştirebilir ve devre dışı bırakabilirsiniz. Bu, testim iddialardan herhangi bir performans etkisi olmadığını göstermesine rağmen, kodu geliştirirken ve test ederken onaylarla doğrulayabileceğiniz ve bunları bir üretim ortamında devre dışı bırakabileceğiniz anlamına gelir.

Bu durumda iddiaları kullanmamak sorun değil çünkü kod sadece başarısız olur, bu da iddiaları kullanırsanız ne olur. Tek fark, iddialarla daha erken, daha anlamlı bir şekilde ve muhtemelen ekstra bilgi ile gerçekleşebilmesidir, bu da beklemediğinizde neden olduğunu anlamanıza yardımcı olabilir.

(1) biraz daha zor. Eğer aradığınız kod üzerinde hiçbir kontrolünüz yoksa o zaman sıkışıp kalırsınız. Null geçerli bir yanıtsa, bunu kontrol etmeniz gerekir.

Bununla birlikte, kontrol ettiğiniz kod ise (ve genellikle durum buysa), o zaman farklı bir hikaye. Yanıt olarak null değerlerini kullanmaktan kaçının. Koleksiyon döndüren yöntemlerle bu kolaydır: hemen hemen boş değerler yerine boş koleksiyonlar (veya diziler) döndürün.

Toplama yapılmadığında daha zor olabilir. Bunu örnek olarak düşünün: Bu arayüzlere sahipseniz:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

Parser, ham kullanıcı girdisi alır ve yapılacak bir şey bulur; Artık uygun bir işlem yapılmazsa sözleşmenin hükümsüz kalmasını sağlayabilirsiniz. Bu, bahsettiğiniz boş denetime yol açar.

Alternatif bir çözüm hiçbir zaman null döndürmemek ve bunun yerine Null Object desenini kullanmaktır :

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

Karşılaştırmak:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

için

ParserFactory.getParser().findAction(someInput).doSomething();

bu çok daha iyi bir tasarıma sahiptir çünkü daha özlü koda yol açar.

Bununla birlikte, findAction () yönteminin anlamlı bir hata mesajıyla bir İstisna atması - özellikle de kullanıcı girişine bağlı olduğunuzda - tamamen uygun olduğunu söyledi. FindAction yönteminin bir istisna atması, çağıran yöntemin açıklamasız basit bir NullPointerException ile patlatmasından çok daha iyi olur.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

Ya da dene / yakala mekanizmasının Hiçbir Şey yapmak yerine çok çirkin olduğunu düşünüyorsanız, varsayılan eyleminiz kullanıcıya geri bildirim sağlamalıdır.

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

631
DO_NOTHING işlemi için yaptığınız ifadelere katılmıyorum. Bul eylem yöntemi bir eylem bulamazsa, null döndürmek doğru olan şeydir. Kodunuzda, kullanılabilir bir eylem bulmak için yöntemin ilkesini ihlal eden gerçekten bulunmayan bir işlem "buldunuz".
MetroidFan2002

266
Java'da özellikle listelerde null değerinin aşırı kullanıldığını kabul ediyorum. Boş bir liste / dizi / koleksiyon null yerine döndürürse çok sayıda apis daha iyi olurdu. Çoğu zaman, bunun yerine istisna atılması gereken yerlerde null kullanılır. Ayrıştırıcı ayrıştırılamazsa bir istisna atılmalıdır.
Laplie Anderson

92
Buradaki ikinci örnek, NR Nesne tasarım deseni olan IIRC'dir.
Steven Evers

62
@Cshah (ve MetroidFan2002). Basit, bunu sözleşmeye koyun ve sonra bulunmayan bir iade eyleminin hiçbir şey yapmayacağı açıktır. Bu, arayan için önemli bilgilerse, bulunmayan bir eylem olduğunu keşfetmek için bir yol sağlayın (örneğin, sonucun DO_NOTHING nesnesi olup olmadığını kontrol etmek için bir yöntem sağlayın). Alternatif olarak, eylem normal olarak bulunursa, yine de null döndürmemeli, bunun yerine özellikle bu durumu gösteren bir istisna atmalısınız - bu yine de daha iyi kodla sonuçlanır. İstenirse, eylem olup olmadığını kontrol etmek için boole döndüren ayrı bir yöntem sağlayın.
Kevin Brock

35
Muhtasar kalite koduna eşit değildir. Öyle düşündüğün için üzgünüm. Kodunuz, bir hatanın avantajlı olacağı durumları gizler.
gshauger

635

JetBrains IntelliJ IDEA , Eclipse veya Netbeans gibi bir Java IDE veya findbugs gibi bir araç kullanıyorsanız (veya kullanmayı planlıyorsanız) , bu sorunu çözmek için ek açıklamaları kullanabilirsiniz.

Temel olarak, @Nullableve var @NotNull.

Aşağıdaki gibi yöntem ve parametrelerde kullanabilirsiniz:

@NotNull public static String helloWorld() {
    return "Hello World";
}

veya

@Nullable public static String helloWorld() {
    return "Hello World";
}

İkinci örnek derlenmeyecektir (IntelliJ IDEA'da).

İlk helloWorld()işlevi başka bir kod parçasında kullandığınızda:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

Şimdi IntelliJ IDEA derleyicisi, kontrolün işe yaramaz olduğunu söyleyecektir, çünkü helloWorld()işlev asla geri dönmeyecektir null.

Parametre kullanma

void someMethod(@NotNull someParameter) { }

şöyle bir şey yazarsanız:

someMethod(null);

Bu derlenmeyecek.

Son örnek @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Bunu yapmak

iWantToDestroyEverything().something();

Ve bunun olmayacağından emin olabilirsiniz. :)

Derleyicinin genellikle olduğundan daha fazla bir şey kontrol etmesine ve sözleşmelerinizin daha güçlü olmasını sağlamanın güzel bir yoludur. Ne yazık ki, tüm derleyiciler tarafından desteklenmiyor.

IntelliJ IDEA 10.5 ve sonraki sürümlerinde, diğer @Nullable @NotNulluygulamalar için destek eklediler .

Blog gönderisine bakın Daha esnek ve yapılandırılabilir @ Nullable / @ NotNull ek açıklamaları .


120
@NotNull, @NullableDiğer nullness ek açıklamalar ve bir parçası olan JSR 305 . Bunları FindBugs gibi araçlarla ilgili olası sorunları tespit etmek için de kullanabilirsiniz .
Jacek S

32
@NotNull& @NullableArayüzlerin pakette yaşaması garip bir şekilde can sıkıcı buluyorum com.sun.istack.internal. (Sanırım com.sun'u tescilli bir API kullanma konusunda uyarılarla ilişkilendiriyorum.)
Jonik

20
kod taşınabilirliği jetbrains ile null gider.Iide seviyesine bağlamadan önce iki kez (kare) düşünürdüm.Like Jacek S bu arada JSR303 olduğunu düşündüğüm herhangi bir şekilde JSR bir parçası olduklarını söyledi.
Java Ka Baby

10
Özel bir derleyici kullanmanın bu soruna uygun bir çözüm olduğunu düşünmüyorum.
Shivan Dragon

64
Ek açıklamalar, hakkında iyi bir şey @NotNullve @Nullableolan kaynak kod bunları anlamadığı bir sistem tarafından inşa edildiğinde onlar güzel aşağılamak olmasıdır. Yani, aslında, kodun taşınabilir olmadığı argümanı geçersiz olabilir - bu ek açıklamaları destekleyen ve anlayan bir sistem kullanırsanız, daha katı hata kontrolünün ek avantajlarından faydalanırsınız, aksi takdirde kodunuz daha az olur, ancak kodunuz yine de Bu ek açıklamalar çalışma zamanında yine de uygulanmadığından, çalışan programınızın kalitesi THE SAME'dir. Ayrıca, tüm derleyiciler özel ;-)
amn

321

Boş değerlere izin verilmiyorsa

Yönteminiz harici olarak çağrılırsa, böyle bir şeyle başlayın:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

Sonra, bu yöntemin geri kalanında objectbunun boş olmadığını bilirsiniz .

Dahili bir yöntemse (API'nın bir parçası değilse), bunun boş olamayacağını belgeleyin, hepsi bu.

Misal:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

Ancak, yönteminiz yalnızca değeri iletirse ve bir sonraki yöntem bunu geçirirse vb. Sorunlu olabilir. Bu durumda, argümanı yukarıdaki gibi kontrol etmek isteyebilirsiniz.

Null değerine izin verilirse

Bu gerçekten bağlıdır. Eğer sık ​​sık böyle bir şey yapmak bulmak:

if (object == null) {
  // something
} else {
  // something else
}

Bu yüzden dallıyorum ve tamamen farklı iki şey yapıyorum. Çirkin kod parçacığı yok, çünkü verilere bağlı olarak gerçekten iki farklı şey yapmam gerekiyor. Örneğin, girdi üzerinde çalışmalı mıyım yoksa iyi bir varsayılan değer mi hesaplamalıyım?


" if (object != null && ..." Deyimini kullanmak benim için çok nadir .

Deyimi tipik olarak nerede kullandığınıza dair örnekler gösterirseniz, size örnek vermek daha kolay olabilir.


94
IllegalArgumentException kuralını atmanın anlamı nedir? Bence NullPointerException daha net olurdu ve null kontrol kendinizi yapmazsanız bu da atılacak. Ya iddialı ya da hiçbir şey kullanmazdım.
Axel

23
Null değerinden başka her değerin kabul edilebilir olması olası değildir. IllegalArgumentException, OutOfRageException vb. Olabilir. Bazen bu mantıklıdır. Diğer zamanlarda, herhangi bir değer katmayan çok sayıda istisna sınıfı oluşturursunuz, o zaman sadece IllegalArgumentException kullanırsınız. Boş girdi için bir istisna, diğer her şey için başka bir istisna olması mantıklı değildir.
myplacedk

7
Evet, başarısız-hızlı prensibi kabul ediyorum, ancak yukarıdaki örnekte, değer aktarılmamış, ancak bir yöntemin çağrılması gereken nesne. Bu yüzden aynı derecede hızlı başarısız olur ve sadece aynı anda ve yerde atılacak bir istisna atmak için boş bir kontrol eklemek hata ayıklamayı daha kolay hale getirmez.
Axel

8
Bir güvenlik açığı mı? JDK bunun gibi kodlarla dolu. Kullanıcının yığın izlerini görmesini istemiyorsanız, yalnızca devre dışı bırakın. Hiç kimse davranışın belgelenmediğini ima etmedi. MySQL, C'de yazılır, burada boş göstergeleri sıfırlamak tanımsız bir davranıştır, istisna atmak gibi bir şey değildir.
fgb

8
throw new IllegalArgumentException("object==null")
Thorbjørn Ravn Andersen

238

Wow, önermek için 57 farklı yolumuz olduğunda başka bir cevap eklemekten neredeyse nefret ediyorum NullObject pattern, ancak bu soruyla ilgilenen bazı kişilerin Java 7 için "null-safe" eklemesi için bir teklif olduğunu bilmek isteyebileceğini düşünüyorum işleme " - eğer eşit değilse null mantık için akıcı bir sözdizimi.

Alex Miller tarafından verilen örnek şöyle:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

?.Sadece de referans bu boş değil ise sol tanımlayıcı araçlar aksi takdirde ifade kalan değerlendirmek null. Java Posse üyesi Dick Wall ve Devoxx'daki seçmenler gibi bazı insanlar bu teklifi gerçekten seviyorlar, ancak aslında nullbir sentinel değeri olarak daha fazla kullanılmasını teşvik edeceği gerekçesiyle de muhalefet var .


Güncelleme: Bir resmi öneri Java 7'de boş güvenlikli operatörü için altında sunulmuştur Proje Coin. Sözdizimi yukarıdaki örnekten biraz farklıdır, ancak aynı kavramdır.


Güncelleme: Geçersiz operatör önerisi onu Project Coin'e dönüştürmedi. Yani, bu sözdizimini Java 7'de görmeyeceksiniz.


20
Bence bu yanlış. Belirli bir değişkenin HER ZAMAN null olmadığını belirtmenin bir yolu olmalıdır.
Thorbjørn Ravn Andersen

5
Güncelleme: teklif Java7 yapmaz. Bkz. Blogs.sun.com/darcy/entry/project_coin_final_five .
Boris Terzic

9
İlginç bir fikir ama sözdizimi seçimi saçma; Her ortak içine soru işaretleri ile dolu bir kod temeli istemiyorum.
Rob

7
Bu operatör Groovy'de var , bu yüzden onu kullanmak isteyenler hala bir seçenek olarak var.
Muhd

8
Bu gördüğüm en ustaca fikir. C sözdiziminin anlamlı her diline eklenmelidir. Ben her yerde "iğneleri işaretlemek" daha doğrusu ekran boyunca satır ilerlemek veya "nöbet yan tümceleri" atlatmak istiyorum.
Victor

196

Tanımlanmamış değerlere izin verilmiyorsa:

IDE'nizi sizi olası boş kayıt silme konusunda uyarması için yapılandırabilirsiniz. Eclipse uygulamasında, bkz. Tercihler> Java> Derleyici> Hatalar / Uyarılar / Boş analiz .

Tanımlanmamış değerlere izin veriliyorsa:

Tanımsız değerlerin anlamlı olduğu yeni bir API tanımlamak istiyorsanız , Seçenek Kalıbı'nı kullanın (işlevsel dillerden aşina olabilir). Aşağıdaki avantajlara sahiptir:

  • Bir giriş veya çıkışın var olup olmadığı API'da açıkça belirtilir.
  • Derleyici, sizi "tanımsız" durumu ele almaya zorlar.
  • Seçenek bir monaddır , bu nedenle ayrıntılı null kontrolüne gerek yoktur, sadece değeri güvenle kullanmak için map / foreach / getOrElse veya benzer bir birleştirici kullanın (örnek) .

Java 8'in yerleşik bir Optionalsınıfı vardır (önerilir); önceki sürümleri için, kütüphane alternatifler vardır örneğin Guava 'nın Optionalveya FunctionalJava ' ın Option. Ancak, birçok işlevsel stil deseninde olduğu gibi, Java'da Option'ı (8 bile) kullanmak, daha az ayrıntılı bir JVM dili, örneğin Scala veya Xtend kullanarak azaltabileceğiniz oldukça küçük bir kazan plakası ile sonuçlanır.

Boş döndürebilecek bir API ile uğraşmanız gerekiyorsa , Java'da fazla bir şey yapamazsınız. Xtend ve Groovy, Elvis operatörüne ?: ve null-güvenli dereference operatörüne sahiptir ?. , ancak null referans durumunda bunun null değerini döndürdüğünü unutmayın, bu nedenle null'un doğru şekilde işlenmesini "defed" eder.


22
Gerçekten, Seçenek deseni harika. Bazı Java eşdeğerleri mevcuttur. Guava, işlevsel öğelerin çoğunu dışarıda bırakan İsteğe bağlı adlı sınırlı bir sürümünü içerir. Haskell'de bu desene Belki denir.
Ben Hardy

9
İsteğe bağlı bir sınıf, Java 8
Pierre Henry'de

1
... ve henüz haritaya veya düzeye sahip değilMap: download.java.net/jdk8/docs/api/java/util/Optional.html
thSoft

3
İsteğe bağlı kalıp hiçbir şeyi çözmez; potansiyel olarak boş bir nesne yerine, şimdi iki tane var.
Kasım'da Boann

1
@Boann, dikkatli kullanılırsa, tüm NPE sorununuzu çözersiniz. Değilse, o zaman bir "kullanım" sorunu var sanırım.
Louis F.

189

Sadece bu durum için -

Eşittir yöntemini çağırmadan önce bir değişkenin null olup olmadığını denetlememek (aşağıdaki dize karşılaştırma örneği):

if ( foo.equals("bar") ) {
 // ...
}

Bir neden olacaktır NullPointerExceptioneğer fooyok.

Bunlarınızı şu şekilde karşılaştırırsanız bundan kaçınabilirsiniz String:

if ( "bar".equals(foo) ) {
 // ...
}

42
Kabul ediyorum - sadece bu durumda. Bunu bir sonraki gereksiz seviyeye götüren programcılara dayanamıyorum ve eğer (null! = MyVar) ... sadece çirkin görünüyor ve hiçbir amaca hizmet etmiyorsa yazamıyorum!
Alex Worden

20
Bu, genel olarak iyi bir uygulamanın muhtemelen en çok kullanılan özel bir örneğidir: eğer biliyorsanız, daima yapın <object that you know that is not null>.equals(<object that might be null>);. equalsSözleşmeyi biliyor olmanız ve bu yöntemlerin nullparametreleri işleyebilmesi dışında başka yöntemler için de çalışır .
Stef

9
Bu aslında mantıklı olan Yoda Koşulları'nı gördüğüm ilk örnektir
Erin Drummond

6
NullPointerExceptions bir nedenden dolayı atılır. Atılırlar çünkü bir nesne olmaması gerektiği yerde boştur. Sorunu gizlemek değil, bunu DÜZELTME programcılar görevidir.
Oliver Watkins

1
Hiçbir şey sorunu gizlemez, bu dilde eksik şekeri çözmek için geçerli bir uygulamadır.
echox

167

Java 8 ile java.util.Optionalproblemin bazılarını tartışmalı olarak çözen yeni sınıf geliyor . En azından kodun okunabilirliğini geliştirdiğini söyleyebilir ve herkese açık API'larda API'nın sözleşmesini istemci geliştirici için daha net hale getirir.

Bu şekilde çalışırlar:

Belirli bir tür ( Fruit) için isteğe bağlı bir nesne, yöntemin dönüş türü olarak oluşturulur. Boş olabilir veya bir Fruitnesne içerebilir :

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Şimdi, belirli bir Fruit örneği için Fruit( fruits) listesini aradığımız bu koda bakın :

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

map()İsteğe bağlı bir nesne üzerinde hesaplama yapmak veya ondan bir değer çıkarmak için işleci kullanabilirsiniz . orElse()eksik değerler için bir geri dönüş sağlamanıza olanak tanır.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

Tabii ki, null / boş değer kontrolü hala gereklidir, ancak en azından geliştirici, değerin boş olabileceğinin ve kontrol etmeyi unutma riskinin sınırlı olduğunun bilincindedir.

OptionalBir dönüş değerinin ne zaman boş olabileceğini kullanarak sıfırdan oluşturulmuş bir API'da ve düz bir nesneyi yalnızca null(kural) olmadığında döndürürken, istemci kodu basit nesne dönüş değerlerinde boş denetimleri bırakabilir ...

Tabii ki Optionalbir yöntem argümanı olarak da kullanılabilir, belki de bazı durumlarda 5 veya 10 aşırı yükleme yönteminden daha fazla isteğe bağlı argüman belirtmenin daha iyi bir yolu.

OptionalorElsevarsayılan değerin kullanılmasına izin veren ve lambda ifadeleriyleifPresent çalışan başka kullanışlı yöntemler sunar .

Sizi NullPointerException(ve genel olarak boş gösterici) sorunlu ve (kısmi) çözümün Optionaliyi açıklandığı bu makaleyi (bu cevabı yazmak için ana kaynağım) okumaya davet ediyorum : Java İsteğe Bağlı Nesneler .


11
Google guava, Java 6+ için isteğe bağlı bir uyarıya sahiptir.
Bradley Gottfried

13
Bu var çok sadece ifPresent ile İsteğe bağlı () kullanarak olmadığını vurgulamak önemlidir değil , normal boş denetimi yukarıda çok değer katar. Temel değeri, başka bir yerde bahsedilen Groovy'de Elvis operatörüne benzer sonuçlar elde eden map / flapMap'in fonksiyon zincirlerinde kullanılabilen bir monad olmasıdır. Bu kullanım olmadan bile, orElse / veyaElseThrow sözdizimini de çok kullanışlı buluyorum.
Cornel Masson


4
Neden insanlar if(optional.isPresent()){ optional.get(); }bunun yerine bunu yapma eğilimindedirleroptional.ifPresent(o -> { ...})
Satyendra Kumar

1
Yani, API sözleşme ipuçlarından başka, gerçekten sadece zincirleme yöntemlerini sonsuza dek zevk alan fonksiyonel programcılara hitap ediyor.
ezmek

125

Ne tür nesneleri denetlediğinize bağlı olarak, apache commons'daki bazı sınıfları aşağıdaki gibi kullanabilirsiniz: apache commons lang ve apache commons collections

Misal:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

veya (neyi kontrol etmeniz gerektiğine bağlı olarak):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

StringUtils sınıfı birçok taneden yalnızca biridir; ortak alanlarda boş, güvenli manipülasyon yapan birkaç iyi sınıf vardır.

Aşağıda, apache kitaplığı (commons-lang-2.4.jar) eklediğinizde JAVA'da null vallidasyonu nasıl kullanabileceğinize ilişkin bir örnek verilmiştir.

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

Spring kullanıyorsanız, Spring paketinde de aynı işlevselliğe sahiptir, bkz. Kütüphane (spring-2.4.6.jar)

Bu statik classf'ın ilkbahardan nasıl kullanılacağına ilişkin örnek (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

5
Ayrıca bulduğum paramları kontrol etmek için yöntemlerin başlangıcında oldukça kullanışlı olan Apache Commons'ın daha genel sürümünü kullanabilirsiniz. Validate.notNull (object, "object null olmamalıdır"); commons.apache.org/lang/apidocs/org/apache/commons/lang/…
monojohnny

@monojohnny Doğrulama Assert deyimlerini kullanır mı? Assert'in JVM'de etkinleştirilebileceği / devre dışı bırakılabileceği ve üretimde kullanılmaması önerilmektedir.
Kurapika

Sanmıyorum - Ben doğrulama başarısız olursa sadece bir RuntimeException atar inanıyorum
monojohnny

96
  • Bir nesnenin boş olmaması gerektiğini (veya bir hata olduğunu) düşünüyorsanız, bir iddia kullanın.
  • Eğer metodunuz null parametrelerini kabul etmiyorsa javadoc'ta söyleyin ve bir iddia kullanın.

! = Null değerini yalnızca nesnenin null olabileceği durumun üstesinden gelmek istiyorsanız kontrol etmeniz gerekir ...

Null / notnull parametrelerine yardımcı olması için Java7'de yeni ek açıklamalar eklemek için bir teklif var: http://tech.puredanger.com/java7/#jsr308



91

Ben "hızlı başarısız" kod hayranıyım. Kendinize sorun - parametrenin boş olması durumunda faydalı bir şey mi yapıyorsunuz? Kodunuzun bu durumda ne yapması gerektiği konusunda net bir cevabınız yoksa ... Yani ilk etapta asla boş olmamalı, sonra yoksayın ve bir NullPointerException oluşturulmasına izin verin. Çağıran kod, bir NPE'yi bir IllegalArgumentException kadar anlamlı hale getirecektir, ancak geliştiricinin, kodunuz beklenmedik bir beklenmedik durum yürütmeye çalışmak yerine bir NPE atıldığında neyin yanlış gittiğini hata ayıklaması ve anlaması daha kolay olacaktır. mantık - sonuçta uygulamanın yine de başarısız olmasına neden olur.


2
iddiaları kullanmak daha iyi, yani Contract.notNull (abc, "abc null olmamalı, xyz sırasında yüklenemedi mi?"); - bu bir if yapmaktan daha kompakt bir yoldur (abc! = null) {yeni RuntimeException atın ...}
ianpojman

76

Google koleksiyonları çerçevesi, sıfır denetimini gerçekleştirmenin iyi ve zarif bir yolunu sunar.

Bir kütüphane sınıfında şöyle bir yöntem vardır:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

Ve kullanımı (ile import static ):

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

Veya örneğinizde:

checkNotNull(someobject).doCalc();

71
mmm, fark nedir? p.getAge () aynı NPE'yi daha az ek yük ve daha net yığın izlemesi ile atacaktır. Neyi kaçırıyorum?
08

15
Örneğinize programcıya yönelik bir istisna olduğunu açıkça belirttiği için bir IllegalArgumentException ("e == null") atmak daha iyidir (bakımcının sorunu tanımlamasına izin vermek için yeterli bilgi ile birlikte). NullPointerExceptions, JVM için ayrılmalıdır, çünkü o zaman bunun kasıtsız olduğunu gösterir (ve genellikle tanımlanması zor bir yerde olur)
Thorbjørn Ravn Andersen

6
Bu artık Google Guava'nın bir parçası.
Steven Benitez

32
Bana aşırı mühendislik gibi geliyor. Sadece JVM'nin bir NPE atmasına izin verin ve kodunuzu bu gereksiz ile karıştırmayın.
Alex Worden

5
Ben onu seviyorum ve argümanlar üzerinde açık kontroller ile çoğu yöntem ve kurucuları açın; bir hata varsa, yöntemler her zaman ilk birkaç satırda başarısız olur ve böyle bir şey bulmadan rahatsız edici referansı biliyorumgetThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");
Cory Kendall

75

Bazen, simetrik bir işlemi tanımlayan parametreleri üzerinde çalışan yöntemleriniz vardır:

a.f(b); <-> b.f(a);

B'nin asla boş olamayacağını biliyorsanız, sadece değiştirebilirsiniz. Eşit olanlar için en kullanışlıdır: foo.equals("bar");Daha iyi yapmak yerine "bar".equals(foo);.


2
Ama sonra equalsnull doğru işleyecek (herhangi bir yöntem olabilir) varsayalım . Gerçekten tüm bu, sorumluluğu bir başkasına (veya başka bir yönteme) iletmektir.
Supericy

4
@Supericy Temel olarak evet, ama equals(veya herhangi bir yöntem) nullyine de kontrol etmek zorunda . Veya açıkça belirtmediğini belirtin.
Angelo Fuchs

74

Kullanımları olan Boş Nesne Kalıbı yerine, boş nesnenin hata olduğu durumları göz önünde bulundurabilirsiniz.

İstisna atıldığında, yığın izlemesini inceleyin ve hata boyunca çalışın.


17
Sorun NullPointerException WHICH değişkeni null olduğunu belirtmez ve satırda birkaç "." - işlemleri olabilir gibi bağlam gevşek genellikle. "İf (foo == null) throw yeni RuntimeException (" foo == null ")" kullanmak, açıkça yanlış olanı belirtmenize olanak tanır ve yığın izlemenizi düzeltmek zorunda olanlara çok daha fazla değer verir.
Thorbjørn Ravn Andersen

1
Andersen ile - Java'nın istisna sisteminin üzerinde çalışılan bir değişkenin adını ekleyebilmesini isterdim, böylece NullPointerExceptions sadece istisnanın oluştuğu satırı değil, aynı zamanda değişken adını da gösterir. Bu, belirsiz yazılımlarda iyi çalışmalıdır.
fwielstra

Henüz çalıştırmayı başaramadım, ancak bu tam olarak bu sorunu çözmeyi amaçlıyor.
MatrixFrog

Sorun nedir? NPE'yi yeterli bağlamın mevcut olduğu uygun seviyede yakalayın, bağlam bilgisini dökün ve istisnayı yeniden düşünün ... Java ile çok kolay.
user1050755

3
Metot çağrısı zincirlemesine karşı vaaz veren bir profesörüm vardı. Onun teorisi, 2 yöntemden daha uzun çağrı zincirlerine karşı dikkatli olmanız gerektiğiydi. Bunun zor bir kural olup olmadığını bilmiyorum, ancak kesinlikle NPE yığın izleriyle ilgili sorunların çoğunu kaldırıyor.
RustyTheBoyRobot

74

Null bir 'sorun' değildir. Komple bir modelleme araç setinin ayrılmaz bir parçasıdır . Yazılım, dünyanın karmaşıklığını modellemeyi amaçlamaktadır ve null yükünü taşımaktadır. Null , Java ve benzerlerinde 'Veri yok' veya 'Bilinmiyor'u belirtir . Bu nedenle, bu amaçlar için null değeri kullanmak uygundur. 'Boş nesne' desenini tercih etmiyorum; Bence ' koruyucuları kim koruyacak ' sorunu yükseliyor .
Bana kız arkadaşımın adının ne olduğunu sorarsan sana kız arkadaşım olmadığını söylerim. Java dilinde null değerini döndüreceğim. Bir alternatif, mümkün olmayan (veya yapamayan) bir sorunu belirtmek için anlamlı bir istisna atmak olacaktır.

  1. 'Bilinmeyen bir soru' için 'bilinmeyen cevap' verin. (İş açısından doğru olduğunda null-güvenli olun) Kullanmadan önce bir yöntemin içinde bir kez null olup olmadığını denetlemek, birden çok arayanın aramadan önce bunları denetlemesini engeller.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }

    Önceki fotoğraf albümümde olmayan bir kız arkadaşın fotoğrafını almak için normal mantık akışına yol açar.

    getPhotoOfThePerson(me.getGirlfriend())

    Ve yeni gelen Java API'sine uyar (ileriye dönük)

    getPhotoByName(me.getGirlfriend()?.getName())

    Bazı kişiler için DB'de depolanan fotoğrafı bulmamak 'normal iş akışı' olsa da, diğer bazı durumlar için aşağıdaki gibi çiftleri kullanırdım

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);

    Ve <alt> + <shift> + <j>yazmaktan nefret etmeyin (Eclipse'de javadoc oluşturun) ve herkese açık API için üç ek kelime yazın. Bu, belgeleri okumayanlar hariç herkes için fazlasıyla yeterli olacaktır.

    /**
     * @return photo or null
     */

    veya

    /**
     * @return photo, never null
     */
  2. Bu oldukça teorik bir durumdur ve çoğu durumda java null güvenli API'yi (10 yıl sonra piyasaya sürülecekse) tercih etmelisiniz, ancak NullPointerExceptionbir Exception. Bu nedenle, Throwablemakul bir uygulamanın yakalamak isteyebileceği koşulları gösteren bir biçimdir ( javadoc )! İstisnaların ilk avantajını ve hata işleme kodunu 'normal' koddan ( Java'nın yaratıcılarına göre ) kullanmak için benim için olduğu gibi yakalamak uygun NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }

    Sorular ortaya çıkabilir:

    S. Ya getPhotoDataSource()null döndürürse?
    C. İş mantığına bağlıdır. Bir fotoğraf albümü bulamazsam size hiç fotoğraf göstermeyeceğim. AppContext başlatılmazsa ne olur? Bu yöntemin iş mantığı bunu ortaya koymaktadır. Aynı mantık daha katı olmalı ve bir istisna atmak iş mantığının bir parçasıdır ve null için açık kontrol kullanılmalıdır (durum 3). Daha iyi burada yeni bir Java Boş güvenli API uyuyor başlatılması için seçici neyi ima ve hangi anlamına gelmez belirtmek için başarısız hızlı programcı hataları durumunda olmak.

    S. Yedek kod yürütülebilir ve gereksiz kaynaklar yakalanabilir.
    C.Bir getPhotoByName()veritabanı bağlantısı açmaya PreparedStatement, kişi adını oluşturup sonunda SQL parametresi olarak kullanmaya çalışabilirdi . Bilinmeyen bir soruya yaklaşım burada bilinmeyen bir cevap verir (durum 1). Kaynakları yakalamadan önce yöntem parametreleri kontrol etmeli ve gerekirse 'bilinmeyen' sonuç döndürmelidir.

    S. Bu yaklaşım, deneme kapanışının açılması nedeniyle bir performans cezasına sahiptir.
    C. Yazılımın öncelikle anlaşılması ve değiştirilmesi kolay olmalıdır. Ancak bundan sonra, performans ve sadece gerekirse düşünülebilir! ve gerektiğinde! ( kaynak ) ve diğerleri).

    PS. Bu yaklaşımın kullanımı , "normal" kod ilkesinden ayrı hata işleme kodunun bir yerde kullanılması makul olduğu kadar makul olacaktır. Bir sonraki örneği düşünün:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }

    PPS. Aşağıya oy vermek için hızlı (ve belgeleri okumak o kadar hızlı değil) için hayatımda hiç boş gösterici istisnası (NPE) yakalamadığımı söylemek istiyorum. Ancak bu olasılık, Java yaratıcıları tarafından bilinçli olarak tasarlanmıştır , çünkü NPE bir alt sınıftır Exception. Biz Java tarihinde bir emsal var ThreadDeathbir olan Erroraslında bir uygulama hatası olduğu için değil ama yakalanmak amaçlanan değildi sadece, çünkü! Ne kadar NPE bir olmak sığar Errordaha ThreadDeath! Ama öyle değil.

  3. Yalnızca iş mantığı bunu ima ediyorsa 'Veri yok' seçeneğini kontrol edin.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }

    ve

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }

    AppContext veya dataSource işlenmemiş çalışma zamanı başlatılmazsa NullPointerException geçerli iş parçacığını öldürür ve Thread.defaultUncaughtExceptionHandler tarafından işlenir (en sevdiğiniz günlükçüyü veya diğer bildirim mekanizmanızı tanımlamanız ve kullanmanız için). Ayarlanmazsa, ThreadGroup # uncaughtException yığın izini sistem hatasına yazdırır. Bir uygulama hata günlüğü izlemek ve aslında uygulama hatası olan her işlenmeyen istisna için Jira sorunu açmak gerekir. Programcı başlatma öğelerinde bir yerde hatayı düzeltmelidir.


6
Yakalamak NullPointerExceptionve geri dönmek nullhata ayıklamak için korkunç. Sonunda NPE ile sonuçlanırsınız ve orijinal olarak null olduğunu anlamak gerçekten zordur.
artbristol

23
İtibarım olsaydı aşağı inerdim. Sadece null değil, aynı zamanda tip sisteminde bir delik. Listeye Ağaç Atama bir tür hatasıdır çünkü ağaçlar List türünün değerleri değildir; aynı mantıkla, null öğesinin atanması bir tür hatası olmalıdır çünkü null, Object türünde bir değer veya bu konu için herhangi bir yararlı tür değildir. Null icat eden adam bile onun "milyar dolarlık hatası" olduğunu düşünür. "T VEYA hiçbir şey tipinde bir değer olabilecek bir değer" kavramı kendi tipidir ve bu şekilde temsil edilmelidir (örn. Belki <T> veya İsteğe Bağlı <T>).
Doval

2
"Belki <T> veya İsteğe Bağlı <T>" olarak hala kod yazmanız gerekir, if (maybeNull.hasValue()) {...}bu yüzden farkı if (maybeNull != null)) {...}nedir?
Mykhaylo Adamovych

1
"NullPointerException'ı yakalamak ve null değerini döndürmek hata ayıklamak için korkunç bir şeydir. NPE ile daha sonra yine de sonuçlanırsınız ve orijinal olarak null olduğunu bulmak gerçekten zordur". Tamamen katılıyorum! Bu gibi durumlarda bir düzine 'if' ifadesi yazmalı ya da iş mantığı yerinde veri ima ediyorsa NPE atamalı veya yeni Java'dan sıfır güvenlikli operatör kullanmalısınız. Ama tam olarak hangi adımı boş bıraktığımı umursamadığım durumlar var. Örneğin, verilerin eksik olabileceğini düşündüğünüzde ekranda göstermeden hemen önce kullanıcı için bazı değerlerin hesaplanması.
Mykhaylo Adamovych

3
@MykhayloAdamovych: Yararınızın sıfır olduğu durumda Maybe<T>veya Optional<T>olmadığı T, ancak hiçbir zaman sıfır olmaması durumunda. Açıkça "bu değer null olabilir - dikkatli kullanın " anlamına gelen bir türünüz varsa ve böyle bir türü tutarlı bir şekilde kullanır ve döndürürseniz T, kodunuzda eski bir düz gördüğünüzde , bunun asla boş olmadığını varsayabilirsiniz. (Elbette, bu derleyici tarafından uygulanabilirse çok daha yararlı olacaktır.)
cHao

71

Java 7, java.util.Objectsüzerinde bir requireNonNull()yöntem olan yeni bir yardımcı program sınıfına sahiptir . Tüm bu yaptığı bir NullPointerExceptionargüman null ise bir atmak , ama kodu biraz temizler. Misal:

Objects.requireNonNull(someObject);
someObject.doCalc();

Yöntem, bir yapıcıdaki bir atamadan hemen önce kontrol etmek için kullanışlıdır , burada her kullanım üç kod satırı kaydedebilir:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

olur

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}

9
Aslında, örneğiniz kod bloatını oluşturur: ilk satır gereksizdir, çünkü NPE ikinci satıra atılacaktır. ;-)
user1050755

1
Doğru. İkinci satır olsaydı daha iyi bir örnek olurdu doCalc(someObject).
Stuart Marks

Bağlı olmak. DoCalc () 'in yazarıysanız, bu yöntemin gövdesine çek eklemenizi öneririm (mümkünse). Ve sonra büyük olasılıkla tekrar null olup olmadığını kontrol etmeye gerek olmayan someObject.someMethod () öğesini çağırırsınız. :-)
user1050755

Eh, yazar değilseniz doCalc()ve null verildiğinde hemen NPE atmazsa, null olup olmadığını kontrol etmeniz ve NPE'yi kendiniz atmanız gerekir. Bunun Objects.requireNonNull()için.
Stuart Marks

7
Sadece kod patlaması değil. Yan etkilere neden olan veya zaman / boşluk kullanan bir yöntemle ön taraftan kontrol etmek daha iyidir.
Rob Grant

51

Sonuçta, bu sorunu tamamen çözmenin tek yolu farklı bir programlama dili kullanmaktır:

  • Objective-C'de, bir yöntemi çağırmanın eşdeğerini yapabilirsiniz nilve kesinlikle hiçbir şey olmayacaktır. Bu, çoğu null denetimi gereksiz kılar, ancak hataları teşhis etmek çok daha zor olabilir.
  • In Nice'in potansiyel null sürümü ve boş değil versiyonu: Bir Java kökenli dil, her türlü iki sürümü vardır. Yalnızca null olmayan türlerde yöntemleri çağırabilirsiniz. Potansiyel olarak null türler, null için açık denetim yapılarak null olmayan türlere dönüştürülebilir. Bu, null kontrollerin gerekli ve nerede olmadıklarını bilmeyi kolaylaştırır.

Woah ... En doğru cevap ve aşağıya inme, adalet nerede? Java'da null her zaman geçerli bir değerdir. Her şeyin bir Nesne - Null bir Her şeydir (elbette burada ilkelleri görmezden geliyoruz, ama fikri anlıyorsunuz). Şahsen Nice tarafından benimsenen yaklaşımı destekliyorum, ancak bunu nullable tiplerde çağrılabilir ve NPE'leri kontrol edilen istisnalara teşvik edebiliriz. Bu, mevcut tüm kodu kıracağı için bir derleyici anahtarı aracılığıyla yapılmalıdır :(
CurtainDog

4
Nice'e aşina değilim, ama Kotlin aynı fikri uygular, dilin tür sistemine null ve null olmayan türler inşa eder. Optionals veya null Object modelinden çok daha özlü.
mtsahakis

38

Gerçekten Java'da yaygın "sorun".

İlk olarak, bu konudaki düşüncelerim:

NULL geçerli bir değer olmadığı yerde NULL geçtiğinde bir şey "yemek" kötü olduğunu düşünüyorum. Bir çeşit hata ile yöntemden çıkmıyorsanız, yönteminizde hiçbir şey yanlış gitmediği anlamına gelir; O zaman muhtemelen bu durumda null döndürürsünüz ve alma yönteminde yine null olup olmadığını tekrar kontrol edersiniz ve asla bitmez ve "if! = Null" vb.

Bu nedenle, IMHO, null, daha fazla yürütmeyi engelleyen kritik bir hata olmalıdır (yani, null geçerli bir değer değildir).

Bu sorunu çözme yolu şudur:

İlk olarak, bu sözleşmeyi takip ediyorum:

  1. Tüm genel yöntemler / API bağımsız değişkenlerini her zaman null için kontrol eder
  2. Tüm özel yöntemler, denetimli yöntemler oldukları için null olup olmadığını denetlemez (yalnızca, yukarıda ele alınmaması durumunda nullpointer istisnasıyla ölsün)
  3. Null değerini denetlemeyen diğer yöntemler yardımcı program yöntemleridir. Herkese açıktırlar, ancak bir nedenle onları ararsanız, hangi parametreleri geçtiğinizi bilirsiniz. Bu, su vermeden su ısıtıcısında suyu kaynatmaya çalışmak gibidir ...

Ve son olarak, kodda, genel yöntemin ilk satırı şu şekilde gider:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

Kontrol etmek için daha fazla parametre ekleyebilmeniz için addParam () öğesinin kendi kendine döndüğünü unutmayın.

Parametrelerden herhangi birinin boş olması durumunda yöntem validate()kontrol edilir ValidationException(işaretlenmiş veya işaretlenmemiş bir tasarım / tat sorunudur, ancak benim ValidationExceptionkontrol edilir).

void validate() throws ValidationException;

Örneğin, "planlar" boşsa, ileti aşağıdaki metni içerir:

" [Plans] parametresi için geçersiz argüman değeri null ile karşılaşıldı "

Gördüğünüz gibi, kullanıcı iletisi için addParam () yöntemindeki (dize) ikinci değer gereklidir, çünkü yansıma olsa bile (yine de bu yazının konusu değil) geçirilen değişken adını kolayca algılayamazsınız.

Ve evet, bu çizginin ötesinde artık boş bir değerle karşılaşmayacağımızı biliyoruz, bu yüzden bu nesneler üzerinde yöntemleri güvenli bir şekilde çağırıyoruz.

Bu şekilde kod temiz, bakımı kolay ve okunabilir.


3
Kesinlikle. Sadece hatayı ve çökmeyi atlayan uygulamalar daha yüksek kalitededir çünkü çalışmadığı zaman şüphe yoktur. Hataları en iyi şekilde yutan uygulamalar incelikle bozulur, ancak genellikle fark edilmesi zor olan ve düzeltilmeyen şekillerde çalışmaz. Ve sorun fark edildiğinde hata ayıklamak çok daha zordur.
Paul Jackson

35

Bu sorunun sorulması, hata işleme stratejileriyle ilgilenebileceğinize işaret eder. Ekibinizin mimarı hataların nasıl çalışacağına karar vermelidir. Bunu yapmanın birkaç yolu vardır:

  1. İstisnaların dalgalanmasına izin verin - bunları 'ana döngüde' veya başka bir yönetim rutininde yakalayın.

    • hata durumlarını kontrol edin ve uygun şekilde kullanın

Tabii ki, Unsur Odaklı Programlamaya da bir göz atın - if( o == null ) handleNull()bayt kodunuza eklemek için düzgün yollar var .


35

Kullanmaya ek olarak assertaşağıdakileri de kullanabilirsiniz:

if (someobject == null) {
    // Handle null here then move on.
}

Bu biraz daha iyidir:

if (someobject != null) {
    .....
    .....



    .....
}

2
Mh, neden? Lütfen herhangi bir defansif hissetmeyin, sadece Java hakkında daha fazla bilgi edinmek istiyorum :)
Matthias Meid

9
@ Genel kural olarak, bir if ifadesindeki ifadeyi "negatif" değil, daha "olumlu" bir ifade olarak tercih ederim. Gördüm Yani eğer if (!something) { x(); } else { y(); }ben refactor eğimli olacak if (something) { y(); } else { x(); }(bir iddia olabilir gerçi o != null... daha olumlu bir seçenektir). Ancak daha da önemlisi, kodun önemli kısmı {}s içine sarılmaz ve yöntemin çoğu için bir seviye daha az girinti vardır. Bunun fastcodejava'nın muhakemesi olup olmadığını bilmiyorum ama bu benim olacaktı.
MatrixFrog

1
Ben de yapmak eğilim budur .. Benim düşüncemde kodu temiz tutar.
Koray Tugay

34

Hiç boş kullanmayın. İzin verme.

Sınıflarımda, çoğu alanın ve yerel değişkenin null olmayan varsayılan değerleri vardır ve kodun her yerine sözleşme ifadeleri (her zaman açık olan ekler) ekliyorum (bunun daha özlü ve izin vermekten daha özlü olduğu için) bir NPE olarak gelir ve ardından satır numarasını vb. çözmek zorunda kalır).

Bu uygulamayı benimsediğimde, sorunların kendiliğinden çözündüğünü fark ettim. Geliştirme sürecinde şeyleri daha erken yakalarsınız ve zayıf bir noktaya sahip olduğunuzu fark edersiniz .. ve daha da önemlisi ... farklı modüllerin endişelerini kapsamaya yardımcı olur, farklı modüller birbirine 'güvenebilir' ve artık çöp kodlaif = null else yapıları !

Bu, savunmacı bir programlamadır ve uzun vadede daha temiz bir kodla sonuçlanır. Her zaman verileri, örneğin burada katı standartlar uygulayarak sterilize edin ve sorunlar ortadan kalkar.

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

Sözleşmeler, üretimde bile her zaman çalışan mini ünite testleri gibidir ve işler başarısız olduğunda, rastgele bir NPE yerine nedenini bir şekilde çözmeniz gerektiğini bilirsiniz.


bu neden reddedilsin ki? tecrübelerime göre, bu diğer yaklaşımlardan çok daha üstündür, neden olmasın bilmek isterdim
ianpojman

Katılıyorum, bu yaklaşım null kodları her yerde kod null kontroller benek düzeltmek yerine, null ile ilgili sorunları önler.
ChrisBlom

7
Bu yaklaşımla ilgili sorun, ad asla ayarlanmazsa, ayarlanmış bir değer gibi davranan "<unknown>" değerine sahip olmasıdır. Şimdi adın hiç ayarlanmadığını kontrol etmeliyim diyelim (bilinmiyor), "<unknown>" özel değeri ile bir dize karşılaştırması yapmak zorundayım.
Steve Kuo

1
Gerçekten iyi bir nokta Steve. Ne sık sık yaptığım bu sabit bir değer var, örneğin kamu statik final String UNSET = "__ unset" ... private String field = UNSET ... sonra private boolean isSet () {return UNSET.equals (field); }
ianpojman

IMHO, Bu kendiliğinden isteğe bağlı (Sözleşme) uygulanması ile Boş Nesne Modelinin bir uygulamasıdır. Kalıcılık sınıfında nasıl davranır? Bu durumda uygulanabilir görmüyorum.
Kurapika

31

Google'ın çok faydalı bir çekirdek kütüphanesi olan Guava'nın, boş değerlerden kaçınmak için güzel ve kullanışlı bir API'si var. UsingAndAvoidingNullExplained çok yararlı buluyorum .

Wiki'de açıklandığı gibi:

Optional<T>null edilebilir bir T referansını null olmayan bir değerle değiştirmenin bir yoludur. İsteğe bağlı, boş olmayan bir T referansı içerebilir (bu durumda referansın "mevcut" olduğunu söyleyebiliriz) veya hiçbir şey içermeyebilir (bu durumda referansın "yok" olduğunu söyleyebiliriz). Asla "null içerdiği" söylenmez.

Kullanımı:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

@CodyGuldner Doğru Cody. Daha fazla bağlam vermek için bağlantıdan alakalı bir teklif verdim.
Murat Derya Özen

25

Bu, her Java geliştiricisi için çok yaygın bir sorundur. Bu nedenle, Java 8'de bu sorunları karmaşık kod olmadan ele almak için resmi destek var.

Java 8 kullanıma sunuldu java.util.Optional<T>. Boş olmayan bir değer içerebilen veya içermeyebilen bir kaptır. Java 8, bazı durumlarda değeri boş olabilecek bir nesneyi işlemek için daha güvenli bir yol sağlamıştır. Haskell ve Scala'nın fikirlerinden esinlenmiştir. .

Özetle, İsteğe bağlı sınıf, bir değerin mevcut veya yok olduğu durumlarla açıkça ilgilenmek için yöntemler içerir. Ancak, boş başvurularla karşılaştırıldığında avantajı, Optional <T> sınıfının sizi değerin olmadığı durum hakkında düşünmeye zorlamasıdır. Sonuç olarak, istenmeyen boş gösterici istisnalarını önleyebilirsiniz.

Yukarıdaki örnekte, evde bulunan birden fazla cihaza tanıtıcı döndüren bir ev hizmeti fabrikamız var. Ancak bu hizmetler kullanılabilir / işlevsel olmayabilir veya olmayabilir; bir NullPointerException ile sonuçlanabileceği anlamına gelir. Boş eklemek yerineifHerhangi bir hizmeti kullanmadan önce koşul bunu İsteğe Bağlı <Hizmet> alanına saralım.

SEÇENEK İÇİN SARMA <T>

Fabrikadan bir servis referansı almak için bir yöntem düşünelim. Hizmet referansını döndürmek yerine, İsteğe Bağlı ile sarın. API kullanıcısının, iade edilen hizmetin kullanılabilir / işlevsel olabileceğini veya kullanılamayacağını bilmesini sağlar.

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

Gördüğünüz gibi Optional.ofNullable()referans sarılmış almak için kolay bir yol sağlar. İsteğe bağlı referansını almanın başka yolları da vardır Optional.empty()& Optional.of(). Biri boş değeri döndürmek yerine boş bir nesneyi döndürmek için, diğeri ise boş bırakılamaz bir nesneyi sarmak için.

Peki NULL KONTROL KAÇINMAK İÇİN NE KADAR YARAR?

Bir başvuru nesnesini tamamladıktan sonra, İsteğe bağlı olarak NPE olmadan sarılmış bir başvuruda yöntemleri çağırmak için birçok yararlı yöntem sağlar.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent, boş bir değerse, belirtilen Tüketiciyi bir referansla çağırır. Aksi takdirde hiçbir şey yapmaz.

@FunctionalInterface
public interface Consumer<T>

Tek bir girdi bağımsız değişkenini kabul eden ve sonuç döndürmeyen bir işlemi temsil eder. Diğer fonksiyonel arayüzlerin çoğundan farklı olarak, Tüketicinin yan etkiler yoluyla çalışması beklenir. Çok temiz ve kolay anlaşılır. Yukarıdaki kod örneğinde,HomeService.switchOn(Service) İsteğe bağlı tutma başvurusu boş değilse çağrılır.

Üçlü işlecini, boş durumu denetlemek için sık sık kullanırız ve alternatif bir değer veya varsayılan değer döndürürüz. İsteğe bağlı, aynı durumu null değerini denetlemeden ele almak için başka bir yol sağlar. Optional.orElse (defaultObj), Optional öğesinin null değeri varsa defaultObj değerini döndürür. Bunu örnek kodumuzda kullanalım:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

Şimdi HomeServices.get () aynı şeyi yapar, ancak daha iyi bir şekilde. Hizmetin önceden başlatılıp başlatılmadığını denetler. Öyleyse aynısını döndürün veya yeni bir Yeni hizmet oluşturun. İsteğe bağlı <T> .orElse (T) varsayılan bir değer döndürmeye yardımcı olur.

Son olarak, NPE ve boş kontrol kodumuz:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

Gönderinin tamamı NPE ve Null check-free kodu… Gerçekten mi? .


Yukarıdaki kodda null bir kontrol var - if(homeServices != null) {değiştirilebilir homeServices.ifPresent(h -> //action);
KrishPrabakar

23

Nat Pryce'nin makalelerini seviyorum. İşte bağlantılar:

Makalelerde ayrıca ilginç bulduğum bir Java Belki Tipi için Git deposuna bir bağlantı var, ancak tek başına kontrol kodu şişmesini azaltabileceğini düşünmüyorum. İnternette biraz araştırma yaptıktan sonra, sanırım ! = Null kod bloat esas olarak dikkatli tasarım ile azaltılabilir.


Michael Feathers, bahsettiğiniz gibi yaklaşımlar hakkında kısa ve ilginç bir metin yazdı
ivan.aguirre

21

Denedim NullObjectPatternama benim için her zaman gitmek için en iyi yol değil. Bazen bir "eylem yok" uygun olmadığında olabilir.

NullPointerExceptiongeliştiricilerin hatası anlamına gelen bir Çalışma Zamanı istisnasıdır ve yeterli deneyime sahip olarak hatanın tam olarak nerede olduğunu söyler.

Şimdi cevaba:

Tüm özniteliklerinizi ve erişimcilerini olabildiğince özel hale getirmeye çalışın veya istemcilere hiç maruz bırakmayın. Elbette yapıcıda argüman değerlerine sahip olabilirsiniz, ancak kapsamı azaltarak istemci sınıfının geçersiz bir değer geçirmesine izin vermezsiniz. Değerleri değiştirmeniz gerekiyorsa, her zaman yeni bir tane oluşturabilirsiniz object. Yapıcıdaki değerleri yalnızca bir kez kontrol edersiniz ve yöntemlerin geri kalanında değerlerin boş olmadığından neredeyse emin olabilirsiniz.

Tabii ki, deneyim bu öneriyi anlamak ve uygulamak için daha iyi bir yoldur.

Bayt!


19

Muhtemelen Java 8 veya daha yenisi için en iyi alternatif Optionalsınıfı kullanmaktır .

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

Bu özellikle olası null değerlerin uzun zincirleri için kullanışlıdır. Misal:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

Null değerine istisna atma örneği:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 Objects.requireNonNull, bir şeyin boş olup olmadığı konusunda kontrol edilmesi gerektiğinde kullanışlı olabilecek yöntemi tanıttı . Misal:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

17

Daha genel olarak cevaplayabilir miyim!

Biz genellikle yöntemler beklediğimizden değil şekilde parametreleri olsun bu sorunu yüz (kötü yöntem çağrısı programcının hatam). Örneğin: bir nesne almayı bekliyorsunuz, bunun yerine bir boş değer elde edersiniz. En az bir karakterli bir String almayı bekliyorsunuz, bunun yerine boş bir String elde edersiniz ...

Yani arasında bir fark yok:

if(object == null){
   //you called my method badly!

}

veya

if(str.length() == 0){
   //you called my method badly again!
}

Her ikisi de başka işlevler yapmadan önce geçerli parametreler aldığımızdan emin olmak istiyorlar.

Diğer bazı cevaplarda belirtildiği gibi, yukarıdaki sorunlardan kaçınmak için Tasarımını sözleşme modeline göre takip edebilirsiniz . Lütfen http://en.wikipedia.org/wiki/Design_by_contract adresine bakın .

Java'da bu kalıbı uygulamak için javax.annotation.NotNull gibi temel java ek açıklamalarını kullanabilir veya Hazırda Bekletme Doğrulayıcısı gibi daha karmaşık kütüphaneler kullanabilirsiniz .

Sadece bir örnek:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

Artık giriş parametrelerini kontrol etmenize gerek kalmadan yönteminizin temel işlevini güvenle geliştirebilirsiniz, yöntemlerinizi beklenmedik parametrelerden korurlar.

Bir adım daha ileri gidebilir ve uygulamanızda yalnızca geçerli pojoların oluşturulabileceğinden emin olabilirsiniz. (hazırda bekleme doğrulama sitesinden örnek)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}

javaxtanım gereği, yok "çekirdek Java".
Tomas

17

Her durumda null nesnelerin kullanılmasını öneren cevapları dikkate almam. Bu model, sözleşmeyi kırabilir ve problemleri çözmek yerine daha derine gömebilir, uygunsuz bir şekilde kullanıldığında, gelecekteki bakım gerektirecek başka bir kazan plakası yığını oluşturacağından bahsetmez.

Gerçekte, bir yöntemden döndürülen bir şey null olabilirse ve çağıran kod bu konuda karar vermek zorundaysa, durumu sağlayan daha erken bir çağrı olmalıdır.

Ayrıca, boş nesne deseninin dikkatsiz kullanılırsa belleğe aç olacağını da unutmayın. Bunun için bir NullObject örneği sahipler arasında paylaşılmalı ve bunların her biri için unigue örneği olmamalıdır.

Ayrıca, türün, ilkel tip temsili olduğu anlamına gelen bu deseni kullanmanızı önermem - matematiksel varlıklar gibi, skaler olmayan: vektörleri, matrisleri, karmaşık sayıları ve durumu tutması gereken POD (Düz Eski Veriler) nesneleri Java yerleşik türleri şeklinde. İkinci durumda, alıcı yöntemlerini rastgele sonuçlarla çağırırsınız. Örneğin, bir NullPerson.getName () yöntemi ne döndürmelidir?

Saçma sonuçlardan kaçınmak için bu tür vakaları düşünmeye değer.


"HasBackground ()" çözümünün bir dezavantajı vardır - iş parçacığı için güvenli değildir. Bir yerine iki yöntemi çağırmanız gerekiyorsa, tüm diziyi çok iş parçacıklı ortamda senkronize etmeniz gerekir.
pkalinow

@pkalinow Sadece bu çözümün bir dezavantajı olduğunu göstermek için tartışmalı bir örnek yaptınız. Kodun çok iş parçacıklı uygulamada çalışması amaçlanmamışsa, dezavantaj yoktur. Ben muhtemelen size iplik güvenli olmayan kodun% 90 koymak olabilir. Burada kodun bu yönü hakkında konuşmuyoruz, tasarım modelinden bahsediyoruz. Ve çoklu iş parçacığı kendi başına bir konudur.
luke1985

Tabii ki tek iş parçacıklı bir uygulamada sorun değil. Bu yorumu verdim çünkü bazen sorun oluyor.
pkalinow

@pkalinow Bu konuyu daha yakından incelerseniz Null Object tasarım deseninin çok iş parçacıklı sorunları çözmeyeceğini öğreneceksiniz. Yani alakasız. Ve dürüst olmak gerekirse, bu modelin güzelce sığacağı yerler buldum, bu yüzden orijinal cevabım aslında biraz yanlış.
luke1985

16
  1. Değişkenleri asla null değerine başlatmayın.
  2. (1) mümkün değilse, tüm koleksiyonları ve dizileri boş koleksiyonlara / dizilere başlatın.

Bunu kendi kodunuzla yapın ve kaçınabilirsiniz! = Boş kontroller.

Çoğu zaman null kontroller döngüleri koleksiyonlar veya diziler üzerinde koruyor gibi görünür, bu yüzden onları boş olarak başlatın, boş kontrollere ihtiyacınız olmayacak.

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

Bunda küçük bir ek yük var, ama daha temiz kod ve daha az NullPointerExceptions için buna değer.



3
+1 Bunu kabul ediyorum. Asla yarı başlatılmış nesneleri döndürmemelisiniz. Jaxb ile ilgili kod ve fasulye kodu bunun için notiroius değildir. Kötü uygulama. Tüm koleksiyonlar başlatılmalı ve tüm nesneler (ideal olarak) null referans olmadan var olmalıdır. İçinde koleksiyonu olan bir nesneyi düşünün. Nesnenin null olmadığını, koleksiyonun null olmadığını ve koleksiyonun null nesneler içermediğini kontrol etmek mantıksız ve aptalca olur.
ggb667

15

Bu, çoğu geliştirici için en yaygın hatadır.

Bunu halletmek için birkaç yolumuz var.

Yaklaşım 1:

org.apache.commons.lang.Validate //using apache framework

notNull (Nesne nesnesi, Dize mesajı)

Yaklaşım 2:

if(someObject!=null){ // simply checking against null
}

Yaklaşım 3:

@isNull @Nullable  // using annotation based validation

Yaklaşım 4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}

Ad. 4. Çok kullanışlı değil - bir işaretçinin boş olup olmadığını kontrol ettiğinizde, muhtemelen üzerine bir yöntem çağırmak istersiniz. Null üzerinde bir yöntemi çağırmak size aynı davranışı verir - NullPointerException.
pkalinow

11
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}

2
Bu yöntemde yanlış olan şey, bence @tltester boşsa varsayılan değer vermek istiyor, bu da mantıklı.
Sawyer

1
Apache comm-lang: 'de böyle bir yöntem vardır ObjectUtils.defaultIfNull(). Bir genel daha var: ObjectUtils.firstNonNull()aşağılayıcı bir strateji uygulamak için kullanılabilir:firstNonNull(bestChoice, secondBest, thirdBest, fallBack);
ivant
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.