Evitarea excepției NullPointerException în Java (Programare, Java, Nullpointerexception, Null)

Eu folosesc object != null foarte mult pentru a evita NullPointerException.

Care este o alternativă la:

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

Comentarii

    131

  • @Shervin Încurajarea nulităților face codul mai puțin ușor de înțeles și mai puțin fiabil. –  > Por Tom Hawtin – tackline.
  • 77

  • Neutilizarea null este superioară majorității celorlalte sugestii de aici. Aruncați excepții, nu returnați sau permiteți null-uri. BTW – cuvântul cheie „assert” este inutil, deoarece este dezactivat în mod implicit. Folosiți un mecanism de eșec activat permanent –  > Por ianpojman.
  • Nuli ar trebui evitate în codul de nivel înalt. Tony Hoare, care a inventat referințele null, le numește „o greșeală de un miliard de dolari”. Aruncați o privire aici pentru câteva idei. –  > Por Andrii Polunin.
  • Se pare că este în Java 8: static Objects.isNull(Object o) docs.oracle.com/javase/8/docs/api/java/java/util/Objects.html –  > Por Robert R Evans.
  • Ce-ar fi să înfășurați obiectul cu Opțional . Folosiți opționalul acolo unde vedeți posibilitatea de a avea nulități –  > Por Abhishek kapoor.
65 răspunsuri

Acest lucru îmi sună ca o problemă rezonabil de comună cu care dezvoltatorii juniori sau intermediari tind să se confrunte la un moment dat: fie nu știu, fie nu au încredere în contractele la care participă și supraverifică defensiv pentru nulls. În plus, atunci când își scriu propriul cod, au tendința de a se baza pe returnarea de nulls pentru a indica ceva, cerând astfel apelantului să verifice dacă există nulls.

În altă ordine de idei, există două cazuri în care apare verificarea nulului:

  1. în cazul în care null este un răspuns valid din punctul de vedere al contractului; și

  2. în cazul în care nu este un răspuns valid.

(2) este ușor. Fie se utilizează assert declarații (aserțiuni) sau permiteți eșecul (de exemplu, NullPointerException). Aserțiunile sunt o caracteristică Java foarte puțin utilizată, care a fost adăugată în versiunea 1.4. Sintaxa este:

assert <condition>

sau

assert <condition> : <object>

unde <condition> este o expresie booleană și <object> este un obiect al cărui toString() rezultatul metodei va fi inclus în eroare.

Un fișier assert aruncă o instrucțiune Error (AssertionError) în cazul în care condiția nu este adevărată. În mod implicit, Java ignoră afirmațiile. Puteți activa aserțiunile prin trecerea opțiunii -ea către JVM. Puteți activa și dezactiva aserțiunile pentru clase și pachete individuale. Acest lucru înseamnă că puteți valida codul cu aserțiuni în timpul dezvoltării și testării și le puteți dezactiva într-un mediu de producție, deși testele mele au arătat că aserțiunile nu au avut aproape niciun impact asupra performanței.

Neutilizarea aserțiunilor în acest caz este în regulă, deoarece codul va eșua pur și simplu, ceea ce se va întâmpla dacă utilizați aserțiunile. Singura diferență este că, în cazul aserțiunilor, acest lucru s-ar putea întâmpla mai devreme, într-un mod mai semnificativ și, eventual, cu informații suplimentare, ceea ce vă poate ajuta să vă dați seama de ce s-a întâmplat dacă nu vă așteptați la acest lucru.

(1) este un pic mai greu. Dacă nu aveți niciun control asupra codului pe care îl apelați, atunci sunteți blocat. Dacă null este un răspuns valid, trebuie să îl verificați.

Totuși, dacă este vorba de un cod pe care îl controlați (și acesta este adesea cazul), atunci este o altă poveste. Evitați să folosiți nulls ca răspuns. În cazul metodelor care returnează colecții, este ușor: returnează colecții goale (sau array-uri) în loc de null-uri aproape tot timpul.

Cu non-colecții s-ar putea să fie mai greu. Luați în considerare acest exemplu: dacă aveți aceste interfețe:

public interface Action {
  void doSomething();
}

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

unde Parser ia datele brute de intrare ale utilizatorului și găsește ceva de făcut, poate dacă implementați o interfață de linie de comandă pentru ceva. Acum ați putea face un contract care să returneze null dacă nu există o acțiune adecvată. Aceasta conduce la verificarea nulă despre care vorbiți.

O soluție alternativă este de a nu returna niciodată null și de a utiliza în schimb modelul Null Object:

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

Comparați:

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

la

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

care este un design mult mai bun, deoarece conduce la un cod mai concis.

Acestea fiind spuse, poate că este foarte potrivit ca metoda findAction() să arunce o excepție cu un mesaj de eroare semnificativ – mai ales în acest caz, în care vă bazați pe datele introduse de utilizator. Ar fi mult mai bine ca metoda findAction să arunce o excepție decât ca metoda apelantă să explodeze cu o simplă NullPointerException fără nicio explicație.

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

Sau, dacă credeți că mecanismul try/catch este prea urât, mai degrabă decât să nu faceți nimic, acțiunea implicită ar trebui să ofere feedback utilizatorului.

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

Comentarii

    642

  • Nu sunt de acord cu afirmațiile dumneavoastră pentru acțiunea DO_NOTHING. Dacă metoda „find action” nu poate găsi o acțiune, atunci returnarea lui null este ceea ce trebuie făcut. Ați „găsit” o acțiune în codul dvs. care nu este cu adevărat găsită, ceea ce încalcă principiul metodei, de a găsi o acțiune utilizabilă. –  > Por MetroidFan2002.
  • 268

  • Sunt de acord că null este folosit în exces în Java, mai ales cu listele. Atât de multe apis ar fi mai bune dacă ar returna o listă / tablou / colecție goală în loc de null. De multe ori, null este folosit acolo unde ar trebui să fie aruncată o excepție în schimb. O excepție ar trebui să fie aruncată în cazul în care parserul nu poate analiza. –  > Por Laplie Anderson.
  • 94

  • Ultimul exemplu de aici este, IIRC, modelul de proiectare Null Object. –  > Por Steven Evers.
  • 63

  • @Cshah (și MetroidFan2002). Simplu, puneți acest lucru în contract și atunci este clar că o acțiune returnată nedescoperită nu va face nimic. Dacă aceasta este o informație importantă pentru apelant, atunci oferiți o modalitate de a descoperi că a fost o acțiune nedezvăluită (de exemplu, oferiți o metodă pentru a verifica dacă rezultatul a fost obiectul DO_NOTHING). În mod alternativ, dacă acțiunea ar trebui să fie găsită în mod normal, atunci nu ar trebui să returnați null, ci să aruncați o excepție care să indice în mod specific această condiție – acest lucru are ca rezultat un cod mai bun. Dacă se dorește, se poate furniza o metodă separată care să returneze un boolean pentru a verifica dacă acțiunea există. –  > Por Kevin Brock.
  • 38

  • Concizia nu înseamnă cod de calitate. Îmi pare rău că sunteți de această părere. Codul dvs. ascunde situații în care o eroare ar fi avantajoasă. –  > Por gshauger.

Dacă folosiți (sau intenționați să folosiți) un IDE Java precum JetBrains IntelliJ IDEA, Eclipse sau Netbeans sau un instrument precum findbugs, atunci puteți utiliza adnotări pentru a rezolva această problemă.

Practic, aveți @Nullable și @NotNull.

Puteți folosi în metodă și în parametri, astfel:

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

sau

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

Al doilea exemplu nu va fi compilat (în IntelliJ IDEA).

Atunci când utilizați primul helloWorld() funcție în altă bucată de cod:

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

Acum, compilatorul IntelliJ IDEA vă va spune că verificarea este inutilă, deoarece funcția helloWorld() nu va returna null, niciodată.

Utilizarea parametrului

void someMethod(@NotNull someParameter) { }

dacă scrieți ceva de genul:

someMethod(null);

Acest lucru nu se va compila.

Ultimul exemplu care utilizează @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Făcând acest lucru

iWantToDestroyEverything().something();

Și puteți fi siguri că acest lucru nu se va întâmpla. 🙂

Este o modalitate plăcută de a lăsa compilatorul să verifice ceva mai mult decât o face de obicei și de a impune contractele dvs. să fie mai puternice. Din păcate, nu este suportat de toate compilatoarele.

În IntelliJ IDEA 10.5 și mai departe, au adăugat suport pentru orice alt tip de @Nullable @NotNull implementări.

A se vedea articolul de pe blog Adnotări @Nullable/@NotNull mai flexibile și configurabile.

Comentarii

    125

  • @NotNull, @Nullable și alte adnotări de nulitate fac parte din JSR 305. De asemenea, le puteți utiliza pentru a detecta potențiale probleme cu instrumente precum FindBugs. –  > Por Jacek S.
  • 33

  • Mi se pare ciudat de enervant faptul că @NotNull & @Nullable trăiesc în pachetul com.sun.istack.internal. (Cred că asociez com.sun cu avertismente privind utilizarea unui API proprietar).  > Por Jonik.
  • 21

  • portabilitatea codului este nulă cu jetbrains.M-aș gândi de două ori(pătrat) înainte de a lega la nivel de ide.Așa cum a spus Jacek S, ei fac parte din JSR în orice caz, care am crezut că a fost JSR303, apropo. –  > Por Java Ka Baby.
  • Chiar nu cred că utilizarea unui compilator personalizat este o soluție viabilă pentru această problemă. –  > Por Shivan Dragon.
  • 67

  • Lucrul bun în ceea ce privește adnotările, care @NotNull și @Nullable sunt, este că acestea se degradează frumos atunci când codul sursă este construit de un sistem care nu le înțelege. Deci, de fapt, argumentul că codul nu este portabil poate fi invalid – dacă folosiți un sistem care suportă și înțelege aceste adnotări, obțineți un beneficiu suplimentar de verificare mai strictă a erorilor, în caz contrar obțineți mai puțin, dar codul dvs. ar trebui să se construiască în continuare bine, iar calitatea programului care rulează este ACEEAȘI, deoarece aceste adnotări nu au fost oricum puse în aplicare în timpul rulării. În plus, toate compilatoarele sunt personalizate 😉 –  > Por amn.

Dacă nu sunt permise valorile nule

Dacă metoda dvs. este apelată din exterior, începeți cu ceva de genul acesta:

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

Apoi, în restul metodei respective, veți ști că object nu este nul.

Dacă este o metodă internă (care nu face parte dintr-o API), documentați doar că nu poate fi nulă, și asta este tot.

Exemplu:

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

Cu toate acestea, dacă metoda dvs. transmite valoarea mai departe, iar următoarea metodă o transmite mai departe etc., ar putea deveni problematic. În acest caz, este posibil să doriți să verificați argumentul ca mai sus.

Dacă este permisă valoarea nulă

Acest lucru depinde. În cazul în care constat că fac adesea ceva de genul următor:

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

Deci, mă ramific și fac două lucruri complet diferite. Nu există un fragment de cod urât, pentru că trebuie să fac două lucruri diferite în funcție de date. De exemplu, ar trebui să lucrez cu datele de intrare sau să calculez o valoare implicită bună?


De fapt, este foarte rar să folosesc idiomul „if (object != null && ...„.

Ar putea fi mai ușor să vă dau exemple, dacă arătați exemple de situații în care folosiți de obicei idiomul.

Comentarii

    96

  • Ce rost are să arunci IllegalArgumentException? Cred că NullPointerException ar fi mai clar, iar aceasta ar fi, de asemenea, aruncată în cazul în care nu faci tu însuți verificarea nulității. Eu aș folosi fie assert, fie nimic. –  > Por Axel.
  • 23

  • Este puțin probabil ca orice altă valoare decât null să fie acceptabilă. Ați putea avea IllegalArgumentException, OutOfRageException etc. etc. Uneori, acest lucru are sens. Alteori, ajungi să creezi o mulțime de clase de excepții care nu adaugă nicio valoare, atunci folosești doar IllegalArgumentException. Nu are sens să ai o excepție pentru null-input și alta pentru orice altceva. –  > Por myplacedk.
  • Da, sunt de acord cu principiul „fail-fast-principle”, dar în exemplul de mai sus, valoarea nu este transmisă, ci este obiectul asupra căruia se va apela o metodă. Prin urmare, eșuează la fel de repede, iar adăugarea unei verificări null doar pentru a arunca o excepție care ar fi fost aruncată oricum în același timp și în același loc nu pare să ușureze depanarea. –  > Por Axel.
  • O gaură de securitate? JDK este plin de astfel de coduri. Dacă nu doriți ca utilizatorul să vadă stacktraces, atunci dezactivați-le. Nimeni nu a insinuat că acest comportament nu este documentat. MySQL este scris în C, unde dereferențierea indicatoarelor nule este un comportament nedefinit, nimic asemănător cu aruncarea unei excepții. –  > Por fgb.
  • throw new IllegalArgumentException("object==null") –  > Por Thorbjørn Ravn Andersen.

Wow, aproape că nu-mi place să adaug un alt răspuns când avem 57 de moduri diferite de a recomanda NullObject pattern, dar cred că unele persoane interesate de această întrebare ar putea dori să știe că există o propunere pe masă pentru Java 7 de a adăuga „manipulare sigură pentru nul”-o sintaxă simplificată pentru logica if-not-equal-null.

Exemplul dat de Alex Miller arată astfel:

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

?. înseamnă că nu se face referire la identificatorul din stânga decât dacă acesta nu este nul, altfel evaluează restul expresiei ca fiind null. Unele persoane, cum ar fi Dick Wall, membru al Java Posse și votanții de la Devoxx iubesc cu adevărat această propunere, dar există și opoziție, pe motiv că, de fapt, va încuraja o mai mare utilizare a null ca valoare santinelă.


Actualizare: Un propunere oficială pentru un operator null-safe în Java 7 a fost depusă în cadrul Proiect Coin. Sintaxa este puțin diferită față de exemplul de mai sus, dar este aceeași noțiune.


Actualizare: Propunerea privind operatorul null-safe nu a fost acceptată în Project Coin. Prin urmare, nu veți mai vedea această sintaxă în Java 7.

Comentarii

    21

  • Cred că acest lucru este greșit. Ar trebui să existe o modalitate de a specifica faptul că o anumită variabilă este ÎNTOTDEAUNA non-null. –  > Por Thorbjørn Ravn Andersen.
  • Actualizare: propunerea nu va ajunge în Java7. A se vedea blogs.sun.com/darcy/entry/project_coin_final_five . –  > Por Boris Terzic.
  • Idee interesantă, dar alegerea sintaxei este absurdă; nu vreau o bază de cod plină de semne de întrebare prinse în fiecare articulație. –  > Por Rob.
  • Acest operator există în Groovy, astfel încât cei care doresc să îl folosească au în continuare această opțiune. –  > Por Muhd.
  • Aceasta este cea mai ingenioasă idee pe care am văzut-o. Ar trebui să fie adăugată la fiecare limbaj sensibil al sintaxei C. Aș prefera să „fixez semne de întrebare” peste tot decât să parcurg o grămadă de linii pe ecran sau să mă feresc de „clauze de gardă” toată ziua. –  > Por Victor.

Dacă valorile nedefinite nu sunt permise:

Ați putea configura IDE-ul pentru a vă avertiza cu privire la o potențială dereferențiere nulă. De exemplu, în Eclipse, consultați Preferences > Java > Compiler > Errors/Warnings/Null analysis (Preferințe > Java > Compilator > Erori/Avertizări/Analiză nulă).

Dacă sunt permise valorile nedefinite:

Dacă doriți să definiți o nouă API în care valorile nedefinite au sens, utilizați opțiunea Opțiunea Model (poate fi cunoscut din limbajele funcționale). Acesta are următoarele avantaje:

  • În API se precizează în mod explicit dacă o intrare sau o ieșire există sau nu.
  • Compilatorul vă obligă să gestionați cazul „nedefinit”.
  • Opțiunea este o monadă, astfel încât nu este nevoie de o verificare verboasă a nulității, trebuie doar să utilizați map/foreach/getOrElse sau un combinator similar pentru a utiliza în siguranță valoarea (exemplu).

Java 8 dispune de o funcție încorporată Optional (recomandată); pentru versiunile anterioare, există alternative de bibliotecă, de exemplu Guava‘s Optional sau FunctionalJava‘s Option. Dar, la fel ca în cazul multor modele de tip funcțional, utilizarea Option în Java (chiar și în 8) are ca rezultat o anumită cantitate de boilerplate, pe care o puteți reduce folosind un limbaj JVM mai puțin verbos, de exemplu Scala sau Xtend.

Dacă trebuie să aveți de-a face cu o API care ar putea să returneze valori nulenu puteți face prea multe în Java. Xtend și Groovy dispun de funcția operatorul Elvis ?: și operatorul operatorul de dereglare sigură a nulului ?., dar rețineți că acesta returnează nul în cazul unei referințe nule, deci nu face decât să „amâne” tratarea corectă a lui null.

Comentarii

    22

  • Într-adevăr, modelul Option este minunat. Există câteva echivalente Java. Guava conține o versiune limitată a acestuia, numită Optional, care exclude majoritatea aspectelor funcționale. În Haskell, acest model se numește Maybe. –  > Por Ben Hardy.
  • @Luca Molteni: Ai perfectă dreptate, am comentat deja la postare pentru a solicita extinderea acestuia. 🙂 –  > Por thSoft.
  • O clasă opțională va fi disponibilă în Java 8 –  > Por Pierre Henry.
  • …și nu are (încă) map și nici flatMap: download.java.net/jdk8/docs/api/java/util/Optional.html –  > Por thSoft.
  • Modelul Optional nu rezolvă nimic; în loc de un obiect potențial nul, acum aveți două. –  > Por Boann.

Doar pentru această situație –

Nu se verifică dacă o variabilă este nulă înainte de a invoca o metodă equals (un exemplu de comparare a șirurilor de caractere de mai jos):

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

va avea ca rezultat un NullPointerException dacă foo nu există.

Puteți evita acest lucru dacă comparați Stringîn felul următor:

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

Comentarii

    42

  • Sunt de acord – doar în această situație. Nu pot suporta programatorii care au dus acest lucru la un nivel inutil și scriu if (null != myVar)… mi se pare urât și nu are niciun scop! –  > Por Alex Worden.
  • 20

  • Acesta este un exemplu particular, probabil cel mai folosit, al unei bune practici generale: dacă știi, fă-o întotdeauna <object that you know that is not null>.equals(<object that might be null>);. Funcționează și pentru alte metode decât equals dacă cunoașteți contractul și dacă aceste metode pot gestiona null parametrii. –  > Por Stef.
  • Acesta este primul exemplu pe care l-am văzut de Condiții Yoda care chiar are sens –  > Por Erin Drummond.
  • NullPointerExceptions sunt aruncate cu un motiv. Ele sunt aruncate pentru că un obiect este nul acolo unde nu ar trebui să fie. Este treaba programatorilor să REZOLVEZE acest lucru, nu să ASCUNDA problema. –  > Por Oliver Watkins.
  • Acest lucru nu face decât să ascundă problema. Ce se întâmplă dacă folosiți foo mai târziu? Dacă o variabilă nu ar trebui să fie nulă, dar este… aplicația dvs. trebuie să primească un NPE, astfel încât dvs., în calitate de dezvoltator, să puteți repara de fapt motivul de bază. –  > Por Arnab Datta.

Odată cu Java 8 vine și noul java.util.Optional clasă care, probabil, rezolvă o parte din problemă. Se poate spune cel puțin că îmbunătățește lizibilitatea codului și, în cazul API-urilor publice, face contractul API-ului mai clar pentru dezvoltatorul client.

Ele funcționează astfel:

Un obiect opțional pentru un anumit tip (Fruit) este creat ca tip de retur al unei metode. Acesta poate fi gol sau poate conține un Fruit obiect:

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

Acum, uitați-vă la acest cod în care căutăm o listă de Fruit (fruits) pentru o anumită instanță Fruit:

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

Puteți folosi funcția map() pentru a efectua un calcul sau pentru a extrage o valoare dintr-un obiect opțional. orElse() vă permite să furnizați o soluție de rezervă pentru valorile lipsă.

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

Bineînțeles, verificarea pentru valoarea nulă/vidă este în continuare necesară, dar cel puțin dezvoltatorul este conștient de faptul că valoarea ar putea fi goală și riscul de a uita să verifice este limitat.

Într-o API construită de la zero folosind Optional ori de câte ori o valoare de retur ar putea fi goală, și returnând un obiect simplu numai atunci când nu poate fi null (convenție), codul clientului ar putea renunța la verificările de nulitate pentru valorile de returnare a obiectelor simple…

Bineînțeles că Optional ar putea fi utilizat și ca argument al unei metode, poate o modalitate mai bună de a indica argumente opționale decât 5 sau 10 metode de supraîncărcare în unele cazuri.

Optional oferă alte metode convenabile, cum ar fi orElse care permit utilizarea unei valori implicite și ifPresent care funcționează cu expresii lambda.

Vă invit să citiți acest articol (sursa mea principală pentru a scrie acest răspuns) în care se prezintă NullPointerException (și, în general, pointerul nul) problematică, precum și soluția (parțială) adusă de Optional sunt bine explicate: Obiecte opționale Java.

Comentarii

  • Google’s guava are un implimention opțional pentru Java 6+. –  > Por Bradley Gottfried.
  • 15

  • Este foarte important să subliniem faptul că utilizarea opționalului numai cu ifPresent() nu nu adaugă prea multă valoare față de o verificare normală a nulității. Valoarea sa de bază constă în faptul că este o monadă care poate fi utilizată în lanțurile de funcții map/flapMap, ceea ce permite obținerea unor rezultate similare cu operatorul Elvis din Groovy menționat în altă parte. Totuși, chiar și fără această utilizare, consider că sintaxa orElse/orElseThrow este, de asemenea, foarte utilă. –  > Por Cornel Masson.
  • Acest blog are o intrare bună despre opțional winterbe.com/posts/2015/03/15/avoid-null-checks-in-java –  > Por JohnC.
  • De ce oamenii au tendința de a face acest lucru if(optional.isPresent()){ optional.get(); } în loc de optional.ifPresent(o -> { ...}) –  > Por Satyendra Kumar.
  • Așadar, în afară de indicii contractuale API, este vorba doar de satisfacerea programatorilor funcționali cărora le place să înlănțuie metode la nesfârșit. –  > Por crush.

În funcție de tipul de obiecte pe care le verificați, este posibil să puteți utiliza unele dintre clasele din apache commons, cum ar fi: apache commons lang și apache commons collections

Exemplu:

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

sau (în funcție de ceea ce trebuie să verificați):

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

Clasa StringUtils este doar una dintre multele clase; există destul de multe clase bune în Commons care permit manipularea sigură a valorii zero.

Urmează un exemplu despre cum puteți utiliza null vallidation în JAVA atunci când includeți biblioteca apache (commons-lang-2.4.jar)

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

Iar dacă folosiți Spring, Spring are, de asemenea, aceeași funcționalitate în pachetul său, vedeți library(spring-2.4.6.jar)

Exemplu de utilizare a acestui classf static din spring(org.springframework.util.Assert)

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

Comentarii

  • De asemenea, puteți utiliza versiunea mai generică de la Apache Commons, destul de utilă la începutul metodelor pentru a verifica parametrii, după părerea mea. Validate.notNull( object, „object must not be null”); commons.apache.org/lang/apidocs/org/apache/commons/lang/… –  > Por monojohnny.
  • @monojohnny Validate folosește declarații Assert în?. întreb asta pentru că Assert poate fi activat / dezactivat pe JVM și este sugerat să nu se folosească în producție. –  > Por Kurapika.
  • Nu cred – cred că doar aruncă o RuntimeException dacă validarea eșuează –  > Por monojohnny.
  • Dacă considerați că un obiect nu ar trebui să fie nul (sau este un bug), utilizați un assert.
  • Dacă metoda dvs. nu acceptă parametri nuli, spuneți-o în javadoc și utilizați un assert.

Trebuie să verificați dacă obiectul != null numai dacă doriți să gestionați cazul în care obiectul poate fi nul…

Există o propunere de adăugare a unor noi adnotări în Java7 pentru a ajuta în cazul parametrilor null / notnull:http://tech.puredanger.com/java7/#jsr308

Comentarii

  • Nu, nu folosiți aserțiuni în codul de producție. –  > Por phil294.

Sunt un fan al codului „fail fast”. Întrebați-vă – faceți ceva util în cazul în care parametrul este nul? Dacă nu aveți un răspuns clar la ceea ce ar trebui să facă codul dvs. în acest caz… De exemplu, nu ar trebui să fie niciodată nul în primul rând, atunci ignorați-l și permiteți aruncarea unei excepții NullPointerException. Codul de apelare va înțelege la fel de bine un NPE ca și o IllegalArgumentException, dar va fi mai ușor pentru dezvoltator să depaneze și să înțeleagă ce nu a mers bine dacă se aruncă un NPE decât dacă codul dumneavoastră încearcă să execute o altă logică de urgență neașteptată – care, în cele din urmă, duce oricum la eșecul aplicației.

Comentarii

  • este mai bine să folosiți aserțiuni, de exemplu Contract.notNull(abc, „abc trebuie să fie non-null, a eșuat încărcarea în timpul xyz?”); – aceasta este o modalitate mai compactă decât să faceți un if (abc!=null) { throw new RuntimeException…}. –  > Por ianpojman.

Cadrul de colecții Google oferă o modalitate bună și elegantă de a realiza verificarea null.

Există o metodă într-o clasă de bibliotecă de genul acesta:

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

Iar utilizarea este (cu import static):

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

Sau în exemplul tău:

checkNotNull(someobject).doCalc();

Comentarii

    72

  • mmm, care este diferența? p.getAge() ar arunca același NPE cu mai puțin efort și cu o urmă de stivă mai clară. Ce îmi scapă? –  > Por mysomic.
  • 16

  • Este mai bine să arunci o IllegalArgumentException(„e == null”) în exemplul tău, deoarece indică în mod clar că este o excepție intenționată de programator (împreună cu suficiente informații pentru a permite de fapt mentenanței să identifice problema). NullPointerExceptions ar trebui să fie rezervate pentru JVM, deoarece astfel se indică în mod clar că acest lucru nu a fost intenționat (și de obicei se întâmplă undeva unde este greu de identificat).  > Por Thorbjørn Ravn Andersen.
  • Acest lucru face acum parte din Google Guava. –  > Por Steven Benitez.
  • 35

  • Mie îmi miroase a suprainginerie. Lăsați JVM-ul să arunce un NPE și nu vă aglomerați codul cu acest gunoi. –  > Por Alex Worden.
  • Mie îmi place și deschid majoritatea metodelor și constructorilor cu verificări explicite ale argumentelor; dacă există o eroare, metodele eșuează întotdeauna în primele linii și știu referința incriminată fără să găsesc ceva de genul getThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi"); –  > Por Cory Kendall.

Mai degrabă decât Null Object Pattern – care are utilizările sale – ați putea lua în considerare situațiile în care obiectul nul este o eroare.

Atunci când excepția este aruncată, examinați urma de stivă și lucrați la eroare.

Comentarii

    17

  • Problema este că, de obicei, pierdeți contextul, deoarece NullPointerException nu indică CE variabilă a fost nulă și este posibil să aveți mai multe operații „.” pe linie. Folosind „if (foo == null) throw new RuntimeException(„foo == null”)”, puteți indica explicit CE a fost greșit, ceea ce conferă urmăririi stivei o valoare mult mai mare pentru cei care trebuie să o repare. –  > Por Thorbjørn Ravn Andersen.
  • Cu Andersen – Mi-ar plăcea ca sistemul de excepții Java să poată include numele unei variabile asupra căreia se lucrează, astfel încât NullPointerExceptions să indice nu numai linia pe care a apărut excepția, ci și numele variabilei. Acest lucru ar trebui să funcționeze foarte bine în software-ul nebifuzat. –  > Por cthulhu.
  • Am avut un profesor care predica împotriva înlănțuirii apelurilor de metode. Teoria lui era că ar trebui să vă feriți de lanțurile de apeluri care sunt mai lungi de 2 metode. Nu știu dacă aceasta este o regulă dură, dar cu siguranță elimină majoritatea problemelor cu urme de stivă NPE. –  > Por RustyTheBoyRobot.

Uneori, aveți metode care operează asupra parametrilor săi care definesc o operație simetrică:

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

Dacă știți că b nu poate fi niciodată nul, puteți să îl schimbați. Acest lucru este cel mai util pentru egalități: În loc de foo.equals("bar"); mai bine face "bar".equals(foo);.

Comentarii

  • Dar atunci trebuie să presupuneți că equals (ar putea fi orice metodă) va gestiona corect valoarea nulă. În realitate, tot ceea ce se face este să se transfere responsabilitatea către altcineva (sau către o altă metodă). –  > Por Supericy.
  • @Supericy În principiu, da, dar equals (sau orice altă metodă) trebuie să verifice dacă null oricum. Sau să precizeze explicit că nu o face. –  > Por Angelo Fuchs.

Null nu este o „problemă”. Este o parte integrantă a unei completă set de instrumente de modelare. Software-ul urmărește să modeleze complexitatea lumii, iar nulul îi poartă povara. Null indică „fără date” sau „necunoscut”. în Java și altele asemenea. Prin urmare, este oportună utilizarea nulităților în aceste scopuri. Eu nu prefer modelul „obiect nul”; cred că acesta crește modelulcine va păzi gardienii‘.
Dacă mă întrebi cum o cheamă pe prietena mea, îți voi spune că nu am nicio prietenă. În limbajul Java, voi returna null. O alternativă ar fi să arunc o excepție semnificativă pentru a indica o problemă care nu poate fi (sau nu vrea să fie) rezolvată chiar acolo și să o deleg undeva mai sus în stivă pentru a încerca din nou sau să raportez utilizatorului o eroare de acces la date.

  1. Pentru o „întrebare necunoscută”, să se dea un „răspuns necunoscut”. (Fiți null-safe în cazul în care acest lucru este corect din punct de vedere comercial) Verificarea argumentelor pentru null o dată în interiorul unei metode înainte de utilizare scutește mai mulți apelanți de verificarea lor înainte de un apel.

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

    Anteriorul conduce la un flux logic normal pentru a obține nicio fotografie a unei prietene inexistente din biblioteca mea foto.

    getPhotoOfThePerson(me.getGirlfriend())
    

    Și se potrivește cu noul API Java care urmează să apară (așteptăm cu nerăbdare).

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

    Deși este mai degrabă un „flux normal de afaceri” să nu găsești o fotografie stocată în BD pentru o anumită persoană, obișnuiam să folosesc perechi ca cele de mai jos pentru alte cazuri

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

    Și să nu-mi displacă să scriu <alt> + <shift> + <j> (generați javadoc în Eclipse) și scrieți trei cuvinte suplimentare pentru API-ul public. Acest lucru va fi mai mult decât suficient pentru toți, cu excepția celor care nu citesc documentația.

    /**
     * @return photo or null
     */
    

    sau

    /**
     * @return photo, never null
     */
    
  2. Acesta este un caz mai degrabă teoretic și în majoritatea cazurilor ar trebui să preferați API-ul java null safe (în cazul în care va fi lansat peste încă 10 ani), dar NullPointerException este o subclasă a unui Exception. Astfel, este o formă de Throwable care indică condițiile pe care o aplicație rezonabilă ar putea dori să le detecteze (javadoc)! Pentru a utiliza primul avantaj cel mai mare al excepțiilor și pentru a separa codul de tratare a erorilor de codul „obișnuit” (conform creatorilor Java) este indicat, ca și în cazul meu, să se prindă NullPointerException.

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

    Ar putea apărea întrebări:

    Q. Ce se întâmplă dacă getPhotoDataSource() returnează null?
    A. Depinde de logica de afaceri. Dacă nu reușesc să găsesc un album foto, nu vă voi arăta nicio fotografie. Ce se întâmplă dacă appContext nu este inițializat? Logica de afaceri a acestei metode suportă acest lucru. În cazul în care aceeași logică ar trebui să fie mai strictă decât să arunce o excepție, aceasta face parte din logica de afaceri și ar trebui utilizată o verificare explicită pentru null (cazul 3). Adresa noua API Java Null-safe se potrivește mai bine în acest caz pentru a specifica selectiv ceea ce implică și ceea ce nu implică să fie inițializat pentru a fi rapid în caz de erori ale programatorului.

    Q. Ar putea fi executat cod redundant și ar putea fi acaparate resurse inutile.
    A. Acest lucru ar putea avea loc dacă getPhotoByName() ar încerca să deschidă o conexiune la o bază de date, să creeze PreparedStatement și, în cele din urmă, să utilizeze numele persoanei ca parametru SQL. Abordarea pentru o întrebare necunoscută oferă un răspuns necunoscut (cazul 1) funcționează aici. Înainte de a prelua resursele, metoda ar trebui să verifice parametrii și să returneze rezultatul „necunoscut” dacă este necesar.

    Q. Această abordare are o penalizare de performanță din cauza deschiderii închiderii try.
    A. Software-ul ar trebui să fie ușor de înțeles și de modificat în primul rând. Abia după aceea se poate gândi la performanță, și numai dacă este necesar! și unde este necesar! (sursa), și multe altele).

    PS. Această abordare va fi la fel de rezonabilă de utilizat ca și cea de la separați codul de tratare a erorilor de codul „obișnuit” este rezonabil să fie folosit în anumite locuri. Luați în considerare următorul exemplu:

    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. Pentru cei care se grăbesc să voteze negativ (și nu atât de repede să citească documentația), aș dori să spun că nu am prins niciodată în viața mea o excepție de null-pointer (NPE). Dar această posibilitate a fost proiectată în mod intenționat de către creatorii Java, deoarece NPE este o subclasă de Exception. Avem un precedent în istoria Java când ThreadDeath este un Error nu pentru că este de fapt o eroare de aplicație, ci exclusiv pentru că nu a fost intenționat să fie prinsă! Cât de mult se potrivește NPE pentru a fi un Error decât ThreadDeath! Dar nu este.

  3. Verificați dacă nu există date numai dacă logica de afaceri implică acest lucru.

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

    și

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

    În cazul în care appContext sau dataSource nu este inițializat, o excepție NullPointerException neacoperită în timp de execuție va distruge firul curent și va fi procesată de către Thread.defaultUncaughtExceptionHandler (pentru ca dumneavoastră să definiți și să utilizați loggerul preferat sau alt mecanism de notificare). Dacă nu este definit, ThreadGroup#uncaughtException va tipări stacktrace la erorile sistemului. Ar trebui să se monitorizeze jurnalul de erori al aplicației și să se deschidă o problemă Jira pentru fiecare excepție netransmisă care este, de fapt, o eroare a aplicației. Programatorul ar trebui să rezolve o eroare undeva în chestiunile de inițializare.

Comentarii

  • Prinderea NullPointerException și returnarea null este îngrozitor de depanat. Se ajunge oricum la NPE mai târziu și este foarte greu să îți dai seama ce a fost inițial nul. –  > Por artbristol.
  • 23

  • Aș da downvote dacă aș avea reputație. Nu numai că null nu este necesar, dar este o gaură în sistemul de tipuri. Atribuirea unui copac la o listă este o eroare de tip, deoarece arborii nu sunt valori de tip Listă; prin aceeași logică, atribuirea lui null ar trebui să fie o eroare de tip, deoarece null nu este o valoare de tip Obiect, sau de orice alt tip util în acest sens. Chiar și omul care a inventat null consideră că aceasta este „greșeala sa de un miliard de dolari”. Noțiunea de „o valoare care ar putea fi o valoare de tip T SAU nimic” este un tip propriu și ar trebui să fie reprezentată ca atare (de exemplu, Maybe<T> sau Optional<T>). –  > Por Doval.
  • În ceea ce privește „Maybe<T> sau Optional<T>”, trebuie să scrieți în continuare cod de genul if (maybeNull.hasValue()) {...} deci care este diferența față de if (maybeNull != null)) {...}? –  > Por Mike.
  • În ceea ce privește „prinderea NullPointerException și returnarea null este oribilă pentru depanare. Se ajunge oricum la NPE mai târziu și este foarte greu să îți dai seama ce a fost inițial nul”. Sunt total de acord! În aceste cazuri, ar trebui să scrieți o duzină de declarații „if” sau să aruncați NPE dacă logica de afaceri implică date pe loc, sau să utilizați operatorul null-safe din noul Java. Dar există cazuri în care nu-mi pasă de pasul exact care mi-a dat null. De exemplu, calcularea unor valori pentru utilizator chiar înainte de a le afișa pe ecran, când vă așteptați ca datele să lipsească. –  > Por Mike.
  • @MykhayloAdamovych: Avantajul Maybe<T> sau Optional<T> nu este în cazul în care T ar putea fi nulă, ci în cazul în care nu ar trebui să fie niciodată nulă. Dacă aveți un tip care înseamnă în mod explicit „această valoare s-ar putea să fie să fie nulă – folosiți-o cu precauție”, și folosiți și returnați un astfel de tip în mod constant, atunci de fiecare dată când vedeți un tip simplu și vechi T în codul dumneavoastră, puteți presupune că nu este niciodată nulă. (Desigur, acest lucru ar fi mult mai util dacă ar fi impus de compilator).  > Por cHao.

Java 7 are un nou element java.util.Objects clasă de utilitate pe care există o requireNonNull() metodă. Tot ceea ce face aceasta este să arunce un NullPointerException dacă argumentul său este nul, dar curăță puțin codul. Exemplu:

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

Metoda este foarte utilă pentru a verifica chiar înainte de o atribuire într-un constructor, unde fiecare utilizare a acesteia poate economisi trei linii de cod:

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

devine

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

Comentarii

  • De fapt, exemplul dvs. constituie o umflătură de cod: prima linie este superfluă deoarece NPE ar fi aruncat în a doua linie. 😉 –  > Por user1050755.
  • Adevărat. Un exemplu mai bun ar fi dacă a doua linie ar fi doCalc(someObject). –  > Por Stuart Marks.
  • Depinde. Dacă sunteți autorul lui doCalc(), v-aș sugera să puneți verificarea în corpul metodei respective (dacă este posibil). Și apoi, cel mai probabil, veți apela someObject.someMethod() unde, din nou, nu este nevoie să verificați dacă este nul. 🙂 –  > Por user1050755.
  • Ei bine, dacă nu sunteți autorul doCalc(), și nu aruncă imediat NPE atunci când este dat null, ar trebui să verificați dacă este null și să aruncați NPE dumneavoastră. Pentru asta Objects.requireNonNull() este pentru. –  > Por Stuart Marks.
  • Nu este vorba doar de umflarea codului. Este mai bine să verifici înainte decât la jumătatea unei metode care provoacă efecte secundare sau folosește timp/spațiu. –  > Por Rob Grant.

În cele din urmă, singura modalitate de a rezolva complet această problemă este utilizarea unui alt limbaj de programare:

  • În Objective-C, puteți face echivalentul invocării unei metode pe nil, și nu se va întâmpla absolut nimic. Acest lucru face ca majoritatea verificărilor null să nu fie necesare, dar poate face ca erorile să fie mult mai greu de diagnosticat.
  • În Frumos, un limbaj derivat din Java, există două versiuni ale tuturor tipurilor: o versiune potențial nulă și o versiune nenulă. Puteți invoca metode numai pe tipurile not-null. Tipurile potențial nule pot fi convertite în tipuri nule prin verificarea explicită a nulității. Astfel, este mult mai ușor să se știe unde sunt necesare verificările de nulitate și unde nu sunt necesare.

Comentarii

  • Nu sunt familiarizat cu Nice, dar Kotlin implementează aceeași idee, având tipurile nullable și not-null încorporate în sistemul de tipuri al limbajului. Mult mai concis decât Optionals sau modelul null Object. –  > Por mtsahakis.

O „problemă” comună în Java, într-adevăr.

În primul rând, gândurile mele în această privință:

Consider că nu este bine să „mănânci” ceva atunci când a fost trecut NULL în cazul în care NULL nu este o valoare validă. Dacă nu ieșiți din metodă cu un fel de eroare, atunci înseamnă că nimic nu a mers prost în metoda dvs. ceea ce nu este adevărat. Atunci, probabil că în acest caz returnezi null, iar în metoda de primire verifici din nou dacă este null, și nu se termină niciodată, și ajungi să ai „if != null”, etc..

Deci, IMHO, null trebuie să fie o eroare critică care împiedică continuarea execuției (adică atunci când null nu este o valoare validă).

Modul în care am rezolvat această problemă este următorul:

În primul rând, urmez această convenție:

  1. Toate metodele publice / API verifică întotdeauna dacă argumentele sale sunt nule.
  2. Toate metodele private nu verifică dacă există null, deoarece sunt metode controlate (doar lasă să moară cu excepția nullpointer în cazul în care nu a fost tratată mai sus).
  3. Singurele alte metode care nu verifică null sunt metodele de utilitate. Acestea sunt publice, dar dacă le apelați dintr-un motiv oarecare, știți ce parametri treceți. Este ca și cum ai încerca să fierbi apă în ceainic fără să oferi apă…

Și, în final, în cod, prima linie a metodei publice sună astfel:

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

Rețineți că addParam() returnează self, astfel încât să puteți adăuga mai mulți parametri de verificat.

Metoda validate() va arunca verificată ValidationException dacă oricare dintre parametri este nul (checked sau unchecked este mai mult o problemă de design/gust, dar metoda mea ValidationException este verificată).

void validate() throws ValidationException;

Mesajul va conține următorul text dacă, de exemplu, „plans” este nul:

S-a întâlnit valoarea nelegală a argumentului null pentru parametrul [plans].

După cum puteți vedea, cea de-a doua valoare din metoda addParam() (string) este necesară pentru mesajul utilizatorului, deoarece nu puteți detecta cu ușurință numele variabilei transmise, nici măcar cu ajutorul reflecției (care oricum nu face obiectul acestei postări…).

Și da, știm că dincolo de această linie nu vom mai întâlni o valoare nulă, așa că vom invoca în siguranță metodele pe aceste obiecte.

În acest fel, codul este curat, ușor de întreținut și de citit.

Comentarii

  • Absolut. Aplicațiile care aruncă pur și simplu eroarea și se prăbușesc sunt de o calitate superioară, deoarece nu există niciun dubiu atunci când nu funcționează. Aplicațiile care înghit erorile în cel mai bun caz se degradează în mod grațios, dar de obicei nu funcționează în moduri care sunt greu de observat și nu sunt reparate. Iar atunci când problema este observată, sunt mult mai greu de depanat. –  > Por Paul Jackson.

În plus față de utilizarea assert puteți utiliza următoarele:

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

Acest lucru este puțin mai bun decât:

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



    .....
}

Comentarii

  • Mh, de ce asta? Te rog să nu te simți în defensivă, aș vrea doar să învăț mai multe despre Java 🙂 –  > Por Matthias Meid.
  • @Mudu Ca regulă generală, prefer ca expresia dintr-o declarație if să fie mai degrabă o declarație „pozitivă”, decât una „negativă”. Așadar, dacă aș vedea if (!something) { x(); } else { y(); } aș fi înclinat să o refactorizez ca fiind if (something) { y(); } else { x(); } (deși se poate argumenta că != null este opțiunea mai pozitivă…). Dar, mai important, partea importantă a codului nu este înfășurată în interiorul lui {}s și aveți un nivel mai puțin de indentare pentru cea mai mare parte a metodei. Nu știu dacă acesta a fost raționamentul lui fastcodejava, dar acesta ar fi al meu. –  > Por MatrixFrog.
  • Aceasta este ceea ce tind să fac și eu… Păstrează codul curat în opinia mea. –  > Por Koray Tugay.
  • @MatrixFrog Da, asta face codul mai curat și mai ușor de citit, deoarece vezi codul important (când totul funcționează fără erori) mai întâi. –  > Por Skaldebane.

Punerea acestei întrebări indică faptul că este posibil să fiți interesat de strategiile de gestionare a erorilor. Cum și unde să gestionați erorile este o întrebare arhitecturală omniprezentă. Există mai multe moduri de a face acest lucru.

Preferata mea: permiteți excepțiilor să se răspândească – prindeți-le în „bucla principală” sau într-o altă funcție cu responsabilități corespunzătoare. Verificarea condițiilor de eroare și tratarea lor în mod corespunzător poate fi considerată o responsabilitate specializată.

Sigur că aruncați o privire și la Programarea orientată pe aspecte – au modalități foarte bune de a introduce if( o == null ) handleNull() în codul de byte.

Doar nu folosiți niciodată null. Nu permiteți acest lucru.

În clasele mele, majoritatea câmpurilor și variabilelor locale au valori implicite non-null și adaug declarații de contract (always-on asserts) peste tot în cod pentru a mă asigura că acest lucru este aplicat (deoarece este mai succint și mai expresiv decât să las să apară un NPE și apoi să trebuiască să rezolvăm numărul liniei etc.).

Odată ce am adoptat această practică, am observat că problemele păreau să se rezolve de la sine. Descoperi lucrurile mult mai devreme în procesul de dezvoltare, doar din greșeală, și îți dai seama că aveai un punct slab… și mai important… ajută la încapsularea preocupărilor diferitelor module, diferite module pot avea „încredere” unele în altele, și nu mai este nevoie de a împrăștia codul cu if = null else construcții!

Aceasta este programare defensivă și are ca rezultat un cod mult mai curat pe termen lung. Întotdeauna curățați datele, de exemplu aici prin aplicarea unor standarde rigide, iar problemele dispar.

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

Contractele sunt ca niște mini-testări unitare care rulează mereu, chiar și în producție, iar atunci când lucrurile eșuează, știți de ce, mai degrabă decât un NPE aleatoriu pe care trebuie să îl descoperiți cumva.

Comentarii

  • De ce ar fi acest lucru criticat? din experiența mea, este mult superior celorlalte abordări, mi-ar plăcea să știu de ce nu…  > Por ianpojman.
  • Sunt de acord, această abordare previne problemele legate de nulls, mai degrabă decât să le rezolve prin specularea verificărilor de cod null peste tot. –  > Por ChrisBlom.
  • Problema cu această abordare este că, dacă numele nu este niciodată setat, are valoarea „<unknown>”, care se comportă ca o valoare setată. Acum, să spunem că trebuie să verific dacă numele nu a fost niciodată setat (necunoscut), trebuie să fac o comparație de șiruri cu valoarea specială „<unknown>”. –  > Por Steve Kuo.
  • Adevărat, bună observație, Steve. Ceea ce fac adesea este să am acea valoare ca o constantă, de exemplu public static final String UNSET=”__unset” … private String field = UNSET … then private boolean isSet() { return UNSET.equals(field); } –  > Por ianpojman.
  • IMHO, Aceasta este o implementare a modelului Null Object Pattern cu o implementare proprie a opționalului (Contract). Cum se comportă în clasa de clasă de persistență? Nu văd cum se aplică în acest caz. –  > Por Kurapika.

Guava, o bibliotecă de bază foarte utilă de la Google, are o API frumoasă și utilă pentru a evita nulitățile. Am constatat că UsingAndAvoidingNullExplained foarte utilă.

Așa cum este explicat în wiki:

Optional<T> este o modalitate de a înlocui o referință T care poate fi nulă cu o valoare care nu este nulă. Un opțional poate conține fie o referință T nenulă (caz în care spunem că referința este „prezentă”), fie că nu conține nimic (caz în care spunem că referința este „absentă”). Nu se spune niciodată că „conține nul”.

Utilizare:

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

Comentarii

  • @CodyGuldner Corect, Cody. Am furnizat un citat relevant din link pentru a oferi mai mult context. –  > Por Murat Derya Özen.

Aceasta este o problemă foarte frecventă pentru fiecare dezvoltator Java. Așadar, există un suport oficial în Java 8 pentru a rezolva aceste probleme fără a aglomera codul.

Java 8 a introdus java.util.Optional<T>. Acesta este un container care poate sau nu poate conține o valoare non-null. Java 8 a oferit o modalitate mai sigură de a gestiona un obiect a cărui valoare poate fi nulă în unele dintre cazuri. Acesta este inspirat din ideile lui Haskell și Scala.

Pe scurt, clasa Optional include metode pentru a trata în mod explicit cazurile în care o valoare este prezentă sau absentă. Cu toate acestea, avantajul în comparație cu referințele nule este că clasa Optional<T> vă obligă să vă gândiți la cazul în care valoarea nu este prezentă. În consecință, puteți preveni excepțiile neintenționate de pointer nul.

În exemplul de mai sus avem o fabrică de servicii pentru locuințe care returnează un handle pentru mai multe aparate disponibile în locuință. Dar aceste servicii pot fi sau nu disponibile/funcționale; aceasta înseamnă că poate duce la o excepție de tip NullPointerException. În loc să se adauge o opțiune null if înainte de a utiliza orice serviciu, să îl includem în Optional<Service>.

WRAPPING TO OPTION<T>

Să luăm în considerare o metodă de obținere a unei referințe a unui serviciu dintr-o fabrică. În loc să returnăm referința serviciului, înveliți-o cu Optional. Aceasta permite utilizatorului API să știe că serviciul returnat poate fi sau nu disponibil/funcțional, utilizați în mod defensiv

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

După cum vedeți Optional.ofNullable() oferă o modalitate ușoară de a obține referința înfășurată. Există și alte modalități de a obține referința opționalului, fie Optional.empty() & Optional.of(). Una pentru a returna un obiect gol în loc să returneze null și, respectiv, cealaltă pentru a înveli un obiect nenulabil.

DECI, CUM ANUME AJUTĂ LA EVITAREA VERIFICĂRII NULITĂȚII?

Odată ce ați înfășurat un obiect de referință, Optional oferă multe metode utile pentru a invoca metode pe o referință înfășurată fără NPE.

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

Optional.ifPresent invocă Consumer-ul dat cu o referință dacă aceasta este o valoare non-nulă. În caz contrar, nu face nimic.

@FunctionalInterface
public interface Consumer<T>

Reprezintă o operațiune care acceptă un singur argument de intrare și nu returnează niciun rezultat. Spre deosebire de majoritatea celorlalte interfețe funcționale, se așteaptă ca Consumer să opereze prin efecte secundare.Este atât de curat și ușor de înțeles. În exemplul de cod de mai sus, HomeService.switchOn(Service) este invocat dacă referința opțională care deține referința nu este nulă.

Utilizăm foarte des operatorul ternar pentru a verifica condiția de nulitate și a returna o valoare alternativă sau o valoare implicită. Opționalul oferă o altă modalitate de a gestiona aceeași condiție fără a verifica nulitatea. Optional.orElse(defaultObj) returnează defaultObj în cazul în care opționalul are o valoare nulă. Să folosim acest lucru în codul nostru de probă:

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

Acum, HomeServices.get() face același lucru, dar într-un mod mai bun. Acesta verifică dacă serviciul este deja inițializat sau nu. Dacă este, atunci returnează același lucru sau creează un nou serviciu nou. Opțional<T>.orElse(T) ajută la returnarea unei valori implicite.

În cele din urmă, iată codul nostru fără NPE și fără verificare de nul:

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){
         //...
    }
}

Postul complet este Codul NPE și Null check-free … Serios?.

Comentarii

  • Există o verificare a nulului în codul de mai sus – if(homeServices != null) { care poate fi schimbat în homeServices.ifPresent(h -> //action); –  > Por KrishPrabakar.

Îmi plac articolele lui Nat Pryce. Iată linkurile:

În articole există, de asemenea, un link către un depozit Git pentru un Java Maybe Type care mi se pare interesant, dar nu cred că singur ar putea scădea blocajul de cod de verificare. După ce am făcut câteva cercetări pe internet, cred că != null ar putea fi diminuat în principal printr-o proiectare atentă.

Comentarii

  • Michael Feathers a scris un text scurt și interesant despre abordări precum cea pe care ați menționat-o dumneavoastră: manuelp.newsblur.com/site/424 –  > Por ivan.aguirre.
  • Dacă este ceva ce am apreciat cel mai mult la acest răspuns, este sfatul „design atent”. Fie că se rezumă la scrierea codului, la remedierea bug-urilor sau la verificarea nulității, este DAMN CRUCIAL să faci un design și o organizare generală bună în cod, pentru a evita multe redundanțe cu care ne confruntăm și astăzi din cauza unor alegeri proaste de design făcute cu mult timp în urmă… –  > Por Skaldebane.

Am încercat NullObjectPattern dar pentru mine nu este întotdeauna cea mai bună cale de urmat. Există uneori când un „no action” nu este potrivit.

NullPointerException este un Excepție în timpul execuției care înseamnă că este vina dezvoltatorului și, cu suficientă experiență, îți spune exact unde este eroarea.

Acum, să trecem la răspuns:

Încercați să faceți toate atributele dvs. și accesorii acestora cât mai privați posibil sau evitați să le expuneți deloc clienților. Puteți avea valorile argumentelor în constructor, desigur, dar prin reducerea domeniului de aplicare nu lăsați clasa client să treacă o valoare invalidă. Dacă aveți nevoie să modificați valorile, puteți oricând să creați un nou fișier object. Verificați valorile doar în constructor o singură dată iar în restul metodelor puteți fi aproape sigur că valorile nu sunt nule.

Desigur, experiența este cea mai bună modalitate de a înțelege și de a aplica această sugestie.

Byte!

Probabil că cea mai bună alternativă pentru Java 8 sau mai nou este să utilizați Optional class.

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

Acest lucru este util mai ales pentru lanțurile lungi de posibile valori nule. Exemplu:

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

Exemplu despre cum se aruncă o excepție la null:

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

Java 7 a introdus funcția Objects.requireNonNull care poate fi utilă atunci când trebuie verificat dacă ceva nu este nul. Exemplu:

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

Pot să vă răspund mai general!

Noi de obicei ne confruntăm cu această problemă atunci când metodele primesc parametrii în modul în care nu ne așteptam (apelarea greșită a metodei este vina programatorului). De exemplu: vă așteptați să obțineți un obiect, în schimb obțineți un null. Vă așteptați să obțineți un String cu cel puțin un caracter, în schimb obțineți un String gol …

Așadar, nu există nicio diferență între:

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

}

sau

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

Amândouă vor să se asigure că am primit parametri valabili, înainte de a efectua orice altă funcție.

După cum s-a menționat în alte răspunsuri, pentru a evita problemele de mai sus, puteți urma Proiectarea prin contract model. Vă rugăm să consultați http://en.wikipedia.org/wiki/Design_by_contract.

Pentru a implementa acest model în java, puteți utiliza adnotările de bază din java, cum ar fi javax.annotation.NotNull sau să utilizați biblioteci mai sofisticate precum Hibernate Validator.

Doar un exemplu:

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

Acum puteți dezvolta în siguranță funcția de bază a metodei dvs. fără a fi nevoie să verificați parametrii de intrare, acestea protejează metodele dvs. de parametrii neașteptați.

Puteți merge un pas mai departe și să vă asigurați că în aplicația dvs. pot fi create numai pojoane valide. (eșantion de pe site-ul hibernate validator)

public class Car {

   @NotNull
   private String manufacturer;

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

   @Min(2)
   private int seatCount;

   // ...
}

Comentarii

  • javax este, prin definiție, nu „core Java”. –  > Por Tomas.

Nu țin cont de răspunsurile care sugerează utilizarea obiectelor null în orice situație. Acest model poate rupe contractul și poate îngropa problemele din ce în ce mai adânc în loc să le rezolve, fără a mai menționa faptul că, folosit în mod necorespunzător, va crea o altă grămadă de cod boilerplate care va necesita întreținere în viitor.

În realitate, dacă un obiect returnat de o metodă poate fi nul și codul apelant trebuie să ia o decizie în acest sens, ar trebui să existe un apel anterior care să asigure starea respectivă.

De asemenea, nu uitați că modelul de obiect nul va fi foarte consumator de memorie dacă este utilizat fără atenție. Din acest motiv, instanța unui NullObject ar trebui să fie partajată între proprietari și nu să fie o instanță unică pentru fiecare dintre aceștia.

De asemenea, nu aș recomanda utilizarea acestui model în cazul în care tipul este menit să fie o reprezentare de tip primitiv – cum ar fi entitățile matematice care nu sunt scalare: vectori, matrici, numere complexe și obiecte POD (Plain Old Data), care sunt menite să păstreze starea sub forma unor tipuri încorporate în Java. În acest din urmă caz, ați ajunge să apelați metode getter cu rezultate arbitrare. De exemplu, ce ar trebui să returneze o metodă NullPerson.getName()?

Merită să luați în considerare astfel de cazuri pentru a evita rezultatele absurde.

Comentarii

  • Soluția cu „hasBackground()” are un singur dezavantaj – nu este thread-safe. Dacă aveți nevoie să apelați două metode în loc de una, trebuie să sincronizați întreaga secvență în mediul multi-threaded. –  > Por pkalinow.
  • @pkalinow Ați făcut un exemplu artificial doar pentru a sublinia faptul că această soluție are un dezavantaj. Dacă codul nu este menit să ruleze în aplicații multithread, atunci nu există niciun dezavantaj. Aș putea să vă pun la dispoziție probabil 90% din codul dvs. care nu este thread safe. Nu vorbim aici despre acest aspect al codului, ci despre modelul de proiectare. Iar multithreading-ul este un subiect de sine stătător. –  > Por luke1985.
  • Bineînțeles că într-o aplicație single-thread nu este o problemă. Am dat acest comentariu pentru că uneori este o problemă. –  > Por pkalinow.
  • @pkalinow Dacă studiezi mai atent acest subiect vei afla că modelul de proiectare Null Object nu va rezolva problemele de multithreading. Deci este irelevant. Și, ca să fiu sincer, am găsit locuri în care acest model s-ar potrivi foarte bine, așa că răspunsul meu inițial este un pic greșit, de fapt. –  > Por luke1985.
  1. Nu inițializați niciodată variabilele la null.
  2. Dacă (1) nu este posibil, inițializați toate colecțiile și array-urile la colecții/array-uri goale.

Făcând acest lucru în propriul cod și poți evita verificările != null.

De cele mai multe ori, verificările de null par să păzească buclele peste colecții sau array-uri, așa că doar inițializați-le goale, nu veți avea nevoie de verificări de null.

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

Acest lucru implică un mic supraîncărcare, dar merită pentru un cod mai curat și mai puține NullPointerExceptions.

Comentarii

  • stackoverflow.com/questions/1386275/… –  > Por Terence.
  • +1 Cu acest lucru sunt de acord. Nu ar trebui să returnați niciodată obiecte pe jumătate inițializate. Codul legat de Jaxb și codul bean este notiroius pentru acest lucru. Este o practică proastă. Toate colecțiile ar trebui să fie inițializate, iar toate obiectele ar trebui să existe fără referințe (ideal) nule. Luați în considerare un obiect care are o colecție în el. Verificarea faptului că obiectul nu este nul, că colecția nu este nulă și că colecția nu conține obiecte nule este nerezonabilă și prostească. –  > Por ggb667.

Aceasta este cea mai frecventă eroare apărută pentru cei mai mulți dintre dezvoltatori.

Avem mai multe modalități de a rezolva această problemă.

Abordarea 1:

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

notNull(Object object object, String message)

Abordarea 2:

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

Abordarea 3:

@isNull @Nullable  // using annotation based validation

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

Comentarii

  • Ad. 4. Nu este foarte utilă – atunci când verificați dacă un pointer este nul, probabil doriți să apelați o metodă asupra acestuia. Apelarea unei metode pe null vă oferă același comportament – NullPointerException. –  > Por pkalinow.
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}

Comentarii

  • Ce este în neregulă cu această metodă, cred că @tltester vrea doar să dea o valoare implicită dacă este nul, ceea ce are sens. –  > Por Sawyer.
  • Există o astfel de metodă în Apache commons-lang: ObjectUtils.defaultIfNull(). Există una mai generală: ObjectUtils.firstNonNull(), care poate fi folosită pentru a implementa o strategie de degradare: firstNonNull(bestChoice, secondBest, thirdBest, fallBack); –  > Por ivant.

Puteți crea o metodă generică pentru obiecte și șiruri de caractere, astfel încât să o puteți utiliza în întreaga aplicație – acest lucru v-ar putea ajuta pe dumneavoastră și pe colegii dumneavoastră: Creați o clasă, de exemplu StringUtilities și adăugați metoda, de exemplu getNullString.

public static String getNullString(Object someobject)
{
   if(null==someobject )
        return null;

   else if(someobject.getClass().isInstance("") && 
          (((String)someobject).trim().equalsIgnoreCase("null")|| 
          ((String)someobject).trim().equalsIgnoreCase("")))
        return null;

   else if(someobject.getClass().isInstance(""))
        return (String)someobject;

   else
        return someobject.toString().trim();
}

Și apelați pur și simplu această metodă ca,

if (StringUtilities.getNullString(someobject) != null)
{ 
  //Do something
}

Cel mai bun mod de a evita verificările Null în Java este să gestionați și să utilizați în mod corespunzător excepțiile. Din experiența mea, verificările Null au devenit mai frecvente și mai necesare pe măsură ce vă apropiați de front-end, deoarece este mai aproape de utilizatorul care poate furniza informații nevalabile prin intermediul interfeței de utilizare (cum ar fi, de exemplu, nici o valoare, fiind trimisă pentru un câmp).

Cineva ar putea argumenta că ar trebui să puteți controla ceea ce face interfața de utilizator, ca să nu uitați că cea mai mare parte a interfeței de utilizator se realizează prin intermediul unei biblioteci terțe de un anumit tip, care, de exemplu, poate returna fie NULL, fie un șir gol pentru o casetă de text goală, în funcție de situație sau de bibliotecă.

Puteți să le combinați pe cele două în felul următor:

try
{
  myvar = get_user_supplied_value(); 
  if (!myvar || myvar.length() == 0) { alert_the_user_somehow(); return; };

  process_user_input(myvar);
} catch (Exception ex) {
  handle_exception(ex);
}

O altă abordare pe care o adoptă oamenii este să spună:

if (myvar && myvar.length() > 0)  { };

Puteți, de asemenea, să aruncați o excepție (ceea ce eu prefer)

if (!myvar || myvar.length() == 0) {
 throw new Exception("You must supply a name!");
};

Dar asta depinde de dumneavoastră.

Iată care este abordarea mea …

class MyObjectHandler
{
public static final int EXCEPTION = (-3);
public static final int INACCESSIBLE = (-2);

public static int doSomething (MyObject obj, MyObjectParameter [] input)
{
    int returnValue= 0;

    try
    {
        if (obj != null)
        {
            returnValue = obj.doSomething(input);
        }
        else
        {
            returnValue = MyObjectHandler.INACCESSIBLE;
        }
    }
    catch (Exception e)
    {
        e.printStack();
        returnValue = MyObjectHandler.EXCEPTION;
    }
    finally
    {
        return returnValue;
    }
}

..

}

Apoi, codul dvs. va fi exact ca:

import xx.xx.xx.MyObjectHandler;
import xx.xx.xx.MyObjectParameter;

class Test
{

    public static void main ()
    {

        MyObject obj = null;

        MyObjectHandler.doSomething(obj, null);

    }

    ..

}

Modelul de obiect nul poate fi folosit ca o soluție pentru această problemă. Pentru aceasta, clasa someObject trebuie modificată.

public abstract class SomeObject {
   public abstract boolean isNil();
}

public class NullObject extends SomeObject {
   @Override
   public boolean isNil() {
      return true;
   }
}
public class RealObject extends SomeObject {
   @Override
   public boolean isNil() {
      return false;
   }
}

Acum, în loc de verificare,

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

putem folosi,

if (!someObject.isNil()) {
   someobject.doCalc();
}

Referință : https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm

Comentarii

  • Nu faceți decât să modificați verificarea. Nu se primește niciun beneficiu real. Chiar dacă am depus eforturi suplimentare pentru a implementa modelul sugerat, codul tot nu este curat. –  > Por Yasin.
  • Dacă implementați modelul Null Object Pattern, în loc să returnați null, ar trebui să returnați NullObject. În acest fel, nu mai aveți nevoie de verificare. Trebuie doar să apelați object.doCalc(). Dacă obiectul este o instanță NullObject, se va aplica instrucțiunea doCalc() nu va face nimic. Va fi doar o implementare goală a metodei. –  > Por Manoel Campos.
  • Practic, îl înlocuiți pe Null cu propria versiune a lui Null, spunând apoi că folosiți această versiune a lui Null, astfel încât să nu mai fiți nevoiți să o folosiți pe cealaltă, care a venit cu limbajul 🙂 –  > Por Dan Chase.

O altă sugestie este să programați defensiv – în care clasele/funcțiile dvs. oferă valori implicite care sunt cunoscute și sigure, iar null este rezervat pentru erori/excepții adevărate.

De exemplu, în loc ca funcțiile care returnează Strings să returneze null atunci când există o problemă (de exemplu, convertirea unui număr într-un string), puneți-le să returneze un String gol („”). Tot trebuie să testați valoarea de returnare înainte de a continua, dar nu ar exista cazuri speciale pentru excepții. Un beneficiu suplimentar al acestui stil de programare este că programul dvs. va fi capabil să diferențieze și să răspundă în mod corespunzător între operațiunile normale și excepții.

Comentarii

  • -1. Asta este chiar mai rău. Acum, în loc să se blocheze dacă uitați să testați, programul dumneavoastră propagă în tăcere o valoare incorectă. –  > Por Melcul mecanic.

În concluzie, pentru a evita declarația

if (object != null) {
    ....
}
  1. începând cu java 7 puteți folosi Objects metode:

    Objects.isNull(obiect)

    Objects.nonNull(obiect)

    Objects.requireNonNull(obiect)

    Objects.equals(obiect1, obiect2)

  2. din java 8 se poate utiliza clasa Optional (când se utilizează)

object.ifPresent(obj -> ...); java 8

object.ifPresentOrElse(obj -> ..., () -> ...); java 9

  1. se bazează pe contractul de metodă (JSR 305) și utilizați Găsiți erori. Marcați-vă codul cu adnotări @javax.annotation.Nullable și @javax.annotation.Nonnnul. De asemenea, sunt disponibile și precondițiile.

    Preconditions.checkNotNull(object);

  2. În cazuri speciale (de exemplu, pentru Strings și Collections), puteți utiliza metodele utilitare apache-commons (sau Google guava):

public static boolean isEmpty(CharSequence cs) //apache CollectionUtils

public static boolean isEmpty(Collection coll) //apache StringUtils

public static boolean isEmpty(Map map map) //apache MapUtils

public static boolean isNullOrEmpty(@Nullable String string) //Guava Strings

  1. Atunci când aveți nevoie să atribuiți o valoare implicită atunci când este nulă, utilizați apache commons lang

public static Object defaultIfNull(Object object object, Object defaultValue)

Comentarii

  • explică foarte bine –  > Por user8478.

Java 8 a introdus o nouă clasă Optional în pachetul java.util.

Avantajele clasei Java 8 Optional:

1.) Nu sunt necesare verificări de nulitate.
2.) Nu mai există NullPointerException în timpul execuției.
3.) Putem dezvolta API-uri curate și ordonate.

Opțional – Un obiect container care poate conține sau nu o valoare non-null. Dacă o valoare este prezentă, isPresent() va returna true, iar get() va returna valoarea.

Pentru mai multe detalii găsiți aici documentația Oracle :-https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

Puteți utiliza FindBugs. Ei au, de asemenea, un Eclipse plugin) care vă ajută să găsiți verificările de nulitate duplicate (printre altele), dar nu uitați că uneori ar trebui să optați pentru o programare defensivă. Există, de asemenea, și Contracts for Java care poate fi de ajutor.

Eu urmez liniile directoare de mai jos pentru a evita verificările null.

  1. Evitați inițializarea leneșă a variabilelor membre pe cât de mult posibil. Inițializați variabilele chiar în declarație. Acest lucru va gestiona excepțiile NullPointerExceptions.

  2. Decideți asupra mutabilitatea variabilelor membre la începutul ciclului. Utilizați construcții de limbaj precum final cuvântul cheie în mod eficient.

  3. Dacă știți că elementele de mărire pentru o metodă nu vor fi modificate, declarați-le ca fiind final.

  4. Limitați numărul mutației a datelor cât mai mult posibil. Unele variabile pot fi create într-un constructor și nu pot fi modificate niciodată. Eliminați metodele setter publice, cu excepția cazului în care sunt cu adevărat necesare.

    De exemplu, să presupunem că o clasă din aplicația dumneavoastră (A.java) întreține o colecție de tipul HashMap. Nu furnizați public metoda getter în A.java și permiteți B.java să se adauge direct un element în Map. În schimb, furnizați o API în A.java, care adaugă un element în colecție.

    // Avoid
    a.getMap().put(key,value)
    
    //recommended
    
    public void addElement(Object key, Object value){
           // Have null checks for both key and value here : single place
           map.put(key,value);
    }
    
  5. Și, în cele din urmă, utilizați try{} catch{} finally{} în locurile potrivite în mod eficient.

Deoarece Java 7 clasa java.util.Objects există.

Dar din moment ce Java 8, puteți utiliza Objects.isNull(var) și Objects.nonNull(var) din Objects pentru a face verificarea pointerului nul.

De exemplu,

String var1 = null;
Date var2 = null;
Long var3 = null;

if(Objects.isNull(var1) && Objects.isNull(var2) && Objects.isNull(var3))
    System.out.println("All Null");
else if (Objects.nonNull(var1) && Objects.nonNull(var2) && Objects.nonNull(var3))
    System.out.println("All Not Null");

Dacă folosiți java8 sau o versiune ulterioară, alegeți metoda isNull(yourObject) de la java.util.Objects.

Exemplu: –

String myObject = null;

Objects.isNull(myObject); //will return true

Utilizare: Codul de mai jos returnează o valoare care nu este nulă (dacă numele nu este nul, atunci va fi returnată valoarea respectivă, altfel va fi returnată valoarea implicită).

final String name = "Jobin";
String nonNullValue = Optional.ofNullable(name).filter(Objects::nonNull).orElse("DefaultName");

Comentarii

  • Acest lucru nu este foarte diferit de myObject == null și a fost introdus în Java8 pentru Predicates în caracteristica lambda. –  > Por PeterMmm.
  • Objects.isNull() este disponibil din Java8 (nu din Java7) –  > Por RealHowTo.
  • @RealHowTo Mulțumesc pentru că ați semnalat acest lucru, am actualizat răspunsul, de fapt Objects este introdus în java7, isNull adăugat la el mai târziu. –  > Por J J J.

Ori de câte ori treceți un array sau un Vector, inițializați-le la cele goale, în loc de null. – În acest fel, puteți evita o mulțime de verificări pentru null și totul este în regulă 🙂

public class NonNullThing {

   Vector vectorField = new Vector();

   int[] arrayField = new int[0];

   public NonNullThing() {

      // etc

   }

}

Încă o alternativă:

Următoarea funcție simplă ajută la ascunderea verificării nulului (nu știu de ce, dar nu am găsit-o ca parte a aceleiași funcții comună bibliotecă):

public static <T> boolean isNull(T argument) {
    return (argument == null);
}

Acum puteți scrie

if (!isNull(someobject)) {
    someobject.doCalc();
}

care este, conform IMO, o modalitate mai bună de exprimare != null.

Comentarii

  • Dacă aveți de gând să negați în mod constant valoarea de returnare, nu ar fi mai bine să scrieți pur și simplu o funcție isNotNull? Nu indică mai clar intenția dumneavoastră? –  > Por TMN.
  • @TMN: aveți dreptate, în mod ideal aș dori să am atât o funcție isNull și isNotNull metodă. –  > Por jeha.
  • 25

  • Nu reușesc să văd cum acest lucru este mai concis decât „someobject != null”. –  > Por user1050755.
  • Instrumentele de dezvoltare în timpul compilării și al execuției, cum ar fi findbugs, vor avea dificultăți mai mari în a identifica verificările de null duplicat. De asemenea, ar fi bine să sperați că Java va include funcția isNull, –  > Por IceArdor.

Consider că Guava Preconditions este foarte util în acest caz. Nu-mi place să las nulls la null pointer exception, deoarece singura modalitate de a înțelege un NPE este localizarea numărului de linie. Numerele de linie din versiunea de producție și versiunea de dezvoltare pot fi diferite.

Folosind Guava Preconditions, pot verifica parametrii nuli și pot defini un mesaj de excepție semnificativ într-o singură linie.

De exemplu,

Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");

Puteți evita cel mai mult pentru a evita NullPointerException doar urmând majoritatea răspunsurilor celorlalți la Întrebare, eu vreau doar să adaug câteva modalități care au fost introduse în Java 9 pentru a gestiona acest scenariu cu grație și, de asemenea, să prezint câteva dintre cele mai vechi care pot fi, de asemenea, utilizate și astfel să vă reduceți eforturile.

  1. public static boolean isNull(Object obj)

    Returnează true dacă referința furnizată este nulă, altfel returneazăfalse.

    Începând cu Java 1.8

  2. public static boolean nonNull(Object obj)

    Returnează true dacă referința furnizată nu este nulă, altfel returneazăfalse.

    Începând cu Java 1.8

  3. public static <T> T requireNonNullElse​(T obj, T defaultObj)

    Returnează primul argument dacă acesta nu este nul, iar în caz contrar returnează al doilea argument care nu este nul.

    Din Java 9

  4. public static <T> T requireNonNullElseGet​(T obj, Supplier<? extends T> supplier)

    Returnează primul argument dacă acesta este nenul, iar în caz contrar returnează valoarea nenulă a furnizorului.get().

    Din Java 9

  5. public static <T> T requireNonNull​(T obj, Supplier<String> messageSupplier)

    Verifică dacă referința obiectului specificat nu este nulă ș i, în caz contrar, aruncă o excepție NullPointerException personalizată.

    Din Java 1.8

Mai multe detalii despre funcțiile de mai sus pot fi găsite aici.

Comentarii

  • puteți vedea că există o mulțime de răspunsuri cu multe upvotes, ce este special la răspunsul dumneavoastră? –  > Por Ibo.
  • Am vrut doar să mai adaug câteva funcții puse la dispoziție cu Java 9. –  > Por Mukesh A.
  • apoi puneți accent pe ea cu un font bold la începutul postării –  > Por Ibo.

Java 8 a introdus o nouă clasă Opțional în java.util pachet. Acesta este folosit pentru a reprezenta o valoare este prezentă sau absentă. Principalul avantaj al acestei noi construcții este că Nu mai există prea multe verificări de null și NullPointerException. Se evită orice tip de execuție NullPointerExceptions și ne ajută să dezvoltăm API-uri sau aplicații Java curate și ordonate. La fel ca Collections și arrays, este, de asemenea, un container care conține cel mult o valoare.

Mai jos sunt câteva linkuri utile pe care le puteți urmări

https://www.mkyong.com/java8/java-8-optional-in-depth/

https://dzone.com/articles/java-8-optional-avoid-null-and

În Java 8 puteți utiliza tipul T pentru tipul de variabilă locală/câmp/câmp/argument de metodă/retur de metodă dacă nu a fost atribuit niciodată null (și nu se verifică pentru null) sau tipul Optional<T> dacă poate fi null. Apoi, utilizați metoda map pentru procesare T -> și metoda flatMap de prelucrare T -> Optional<R>:

class SomeService {
    @Inject
    private CompanyDao companyDao;

    // return Optional<String>
    public Optional<String> selectCeoCityByCompanyId0(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity);
    }

    // return String + default value
    public String selectCeoCityByCompanyId1(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElse("UNKNOWN");
    }

    // return String + exception
    public String selectCeoCityByCompanyId2(int companyId) throws NoSuchElementException {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElseThrow(NoSuchElementException::new);
    }
}

interface CompanyDao {
    // real situation: no company for such id -> use Optional<Company> 
    Optional<Company> selectById(int id);
}

class Company {
    // company always has ceo -> use Person 
    Person ceo;
    public Person getCeo() {return ceo;}
}

class Person {
    // person always has name -> use String
    String firstName;
    // person can be without address -> use Optional<Address>
    Optional<Address> homeAddress = Optional.empty();

    public String getFirstName() {return firstName;}   
    public Optional<Address> getHomeAddress() {return homeAddress;}
}

class Address {
    //  address always contains country -> use String
    String country;
    //  city field is optional -> use Optional<String>
    Optional<String> city = Optional.empty();

    String getCountry() {return country;}    
    Optional<String> getCity() {return city;}
}

Puteți utiliza un interceptor înainte de apelarea metodei. Aceasta este ceea ce programarea orientată pe aspecte se concentrează pe.

Să presupunem că M1(Object test) este o metodă și M2 este o metodă în care aplicăm un aspect înainte de apelul metodei, M2(Object test2). Dacă test2 != null atunci se apelează M1, altfel se face un alt lucru. Acest lucru funcționează pentru toate metodele la care doriți să aplicați un aspect. Dacă doriți să aplicați un aspect pentru un câmp de instanță și un constructor, puteți utiliza AspectJ. Spring poate fi, de asemenea, cea mai bună alegere pentru un aspect de metodă.

Comentarii

  • propuneți să interceptați fiecare metodă din întreaga aplicație? –  > Por Kurapika.

Java 8 are acum clasa Optional care înfășoară obiectul în considerare și dacă o valoare este prezentă, isPresent() va returna true și get() va returna valoarea.

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

De asemenea, puteți utiliza Checker Framework (cu JDK 7 și ulterior) pentru a verifica static dacă există valori nule. Acest lucru ar putea rezolva o mulțime de probleme, dar necesită rularea unui instrument suplimentar care, în prezent, funcționează numai cu OpenJDK AFAIK. https://checkerframework.org/

Cu Java 8, ați putea trece un furnizor către o metodă de ajutor ca mai jos,

if(CommonUtil.resolve(()-> a.b().c()).isPresent()) {

}

Mai sus înlocuiește codul de tip boiler plate ca mai jos,

if(a!=null && a.b()!=null && a.b().c()!=null) {

}

//CommonUtil.java

 public static <T> Optional<T> resolve(Supplier<T> resolver) {
        try {
            T result = resolver.get();
            return Optional.ofNullable(result);
        } catch (NullPointerException var2) {
            return Optional.empty();
        }
    }

O altă alternativă la verificarea != null este (dacă nu puteți scăpa de ea din punct de vedere al designului):

Optional.ofNullable(someobject).ifPresent(someobject -> someobject.doCalc());

sau

Optional.ofNullable(someobject).ifPresent(SomeClass::doCalc);

Cu SomeClass fiind tipul unui anumit obiect.

Cu toate acestea, nu puteți obține o valoare de retur din doCalc(), deci este utilă numai pentru metodele void.

Comentarii

  • Puteți obține valoarea folosind map combinat cu get sau oricare dintre celelalte metode de obținere a valorii din Opțional, în loc de ifPresent. –  > Por ThisIsNoZaku.
public class Null {

public static void main(String[] args) {
    String str1 = null;
    String str2 = "";

    if(isNullOrEmpty(str1))
        System.out.println("First string is null or empty.");
    else
        System.out.println("First string is not null or empty.");

    if(isNullOrEmpty(str2))
        System.out.println("Second string is null or empty.");
    else
        System.out.println("Second string is not null or empty.");
}

public static boolean isNullOrEmpty(String str) {
    if(str != null && !str.isEmpty())
        return false;
    return true;
}
}

Ieșire

str1 is null or empty.
str2 is null or empty.

În programul de mai sus, avem două șiruri de caractere str1 și str2. str1 conține o valoare nulă, iar str2 este un șir gol.

Am creat, de asemenea, o funcție isNullOrEmpty() care verifică, așa cum sugerează și numele, dacă șirul este nul sau gol. Aceasta verifică dacă este nul folosind metoda != null și isEmpty() a șirului.

În termeni simpli, dacă un șir de caractere nu este nul și isEmpty() returnează false, înseamnă că nu este nici nul, nici gol. În caz contrar, este.

Cu toate acestea, programul de mai sus nu returnează gol dacă un șir de caractere conține doar caractere de spații albe (spații). Din punct de vedere tehnic, isEmpty() vede că acesta conține spații și returnează false. Pentru șirurile de caractere cu spații, folosim metoda string trim() pentru a elimina toate caracterele de spațiu alb din față și din spate.

Modul de a evita spațiile inutile null-checks este simplu de precizat:

You need to know which variables can be null, and which cannot, and you need to be confident about which category a given variable fall into.

Dar, deși poate fi enunțată destul de simplu, realizarea ei este mai dificilă. Cheia se află în confident parte, deoarece cum puteți fi siguri că o variabilă nu poate fi nulă?

Nu există răspunsuri rapide și ușoare la această problemă, dar iată câteva indicații:

  1. Cod curat. Cel mai important lucru pentru a putea raționa asupra comportamentului unei bucăți de cod este ca acesta să fie scris într-o chestiune ușor de înțeles. Numiți variabilele în funcție de ceea ce reprezintă, denumiți-vă metodele după ceea ce fac, aplicați Single responsibility principle (se aplică S în SOLID: http://en.wikipedia.org/wiki/SOLID_(object-oriented_design), înseamnă că fiecare bucată de cod trebuie să aibă o singură responsabilitate și să facă asta și nimic altceva). Odată ce codul dumneavoastră este curat, este mult mai ușor să raționați asupra lui, de asemenea, pe mai multe niveluri/straturi de cod. Cu un cod dezordonat, încercarea de a înțelege ce face o metodă ar putea să vă facă să uitați de ce citiți metoda în primul rând. (Sfat: Citiți „Clean Code” de Robert C. Martin)

  2. Evitați să returnați null valori. În cazul în care o null valoare ar împiedica programul să funcționeze corect, aruncați o exception în schimb (asigurați-vă că adăugați gestionarea corespunzătoare a erorilor.) Cazurile în care returnarea unei valori null valoare ar putea fi acceptabil este, de exemplu, încercarea de a prelua un obiect din baza de date. În aceste cazuri, scrieți un cod care să gestioneze null valori, și notați după ureche că aici avem ceva care ar putea returna null. Gestionați valoarea returnată null cât mai aproape de apelantul metodei care returnează valorile returnate. null cât mai aproape posibil de cel care returnează metoda care returnează (nu le transmiteți orbește în lanțul de apeluri).

  3. Nu treceți niciodată NICIODATĂ valori explicite null ca parametri (cel puțin nu între clase). Dacă vă aflați vreodată într-o situație în care trebuie să treceți o valoare null-este singura opțiune, trebuie să creați o nouă metodă care să nu aibă acest parametru.

  4. Validați-vă datele de intrare! Identificați „punctele de intrare” în aplicația dumneavoastră. Acestea pot fi orice, de la servicii web, servicii REST, clase EJB la distanță, controlori etc. Pentru fiecare metodă din aceste puncte de intrare, întrebați-vă: „Se va executa corect această metodă dacă acest parametru este nul?”. Dacă răspunsul este nu, adăugați Validate.notNull(someParam, "Can't function when someParam is null!");. Acest lucru va arunca o eroare IllegalArgumentException în cazul în care parametrul necesar lipsește. Partea bună a acestui tip de validare în punctele de intrare este că puteți presupune cu ușurință, în codul executat din punctul de intrare, că această variabilă nu va fi niciodată nulă! De asemenea, în cazul în care acest lucru eșuează, fiind la punctul de intrare, depanarea este mult mai ușoară decât ar fi fost dacă ați fi primit doar un NullPointerException adânc în cod, deoarece un astfel de eșec nu poate însemna decât un singur lucru: clientul nu v-a trimis toate informațiile necesare. În cele mai multe cazuri, doriți să validați toți parametrii de intrare, dacă vă aflați într-o poziție în care trebuie să permiteți o mulțime de null-valori, ar putea fi un semn al unei interfețe prost proiectate, care are nevoie de refactorizare/adăugiri pentru a răspunde nevoilor clienților.

  5. Atunci când se lucrează cu Collections, returnați mai degrabă o valoare goală decât una nulă!

  6. Atunci când lucrați cu o bază de date, utilizați not null-constraints. În acest fel, veți ști că o valoare citită din baza de date nu poate fi nulă și nu va trebui să verificați acest lucru.

  7. Structurați-vă codul și respectați-l. Acest lucru vă permite să faceți presupuneri cu privire la comportamentul codului, de exemplu, dacă toate datele de intrare în aplicația dvs. sunt validate, atunci puteți presupune că aceste valori nu vor fi niciodată nule.

  8. Dacă nu faceți deja acest lucru, scrieți teste automate ale codului dumneavoastră. Scriind teste, veți raționa despre codul dvs. și, de asemenea, veți deveni mai încrezător că acesta face ceea ce trebuie să facă. De asemenea, testele automate vă feresc de gafe în timpul refactorizării, anunțându-vă imediat că această bucată de cod nu mai face ceea ce făcea înainte.

Desigur, mai trebuie să verificați în continuare dacă există un null-check, dar acest lucru poate fi redus la minimum (de exemplu, situația în care știe că s-ar putea să obțineți o valoare nulă, în loc de peste tot doar pentru a fi siguri). În ceea ce privește verificările de nulitate, prefer să folosesc operatorul ternar (dar folosiți-l cu grijă, pentru că, atunci când începeți să le aninați, acestea devin foarte complicate).

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "null";
}

OK, acum știu că acest lucru a primit un răspuns tehnic de un milion de ori, dar trebuie să spun asta pentru că este o discuție fără sfârșit cu programatorii Java.

Îmi pare rău, dar nu sunt de acord cu aproape toate cele de mai sus. Motivul pentru care trebuie să testăm pentru null în Java este că programatorii Java nu știu cum să gestioneze memoria.

Spun asta pentru că am o experiență îndelungată de programare în C++ și nu facem asta. Cu alte cuvinte, nu este nevoie să o faceți. Și rețineți că, în Java, dacă atingeți un pointer care atârnă, obțineți o excepție normală; în C++, această excepție în mod normal nu este prinsă și termină programul.

Nu doriți să faceți acest lucru? Atunci urmați câteva reguli simple ala C/C++.

Nu instanțiați lucrurile atât de ușor, gândiți-vă la că fiecare „nou” vă poate aduce o mulțime de probleme și RESPECTAȚI aceste reguli simple.

O clasă trebuie să acceseze memoria în doar 3 moduri ->

  1. Poate „AVEA” membri de clasă, iar aceștia vor respecta aceste reguli:

    1. TOȚI membrii „ARE” sunt creați „new” în constructor.
    2. Se vor închide /de aloca în destructor sau în funcția echivalentă close()în Java pentru aceeași clasă și în NICIUN alt caz.

Acest lucru înseamnă că trebuie să aveți în vedere (la fel ca în Java) cine este proprietarul sau părintele fiecărei resurse și să respectați această proprietate. Un obiect este șters numai de clasa care l-a creat. De asemenea, ->

  1. Unii membri vor fi „UTILIZAȚI”, dar nu dețin sau „AU”. Aceștia sunt „PROPRIU” într-o altă clasă și sunt trecuți ca argumente la constructor. Deoarece aceștia sunt deținuți de o altă clasă, nu vom șterge sau închide NICIODATĂ acest lucru, doar părintele poate.

  2. O metodă dintr-o clasă poate, de asemenea, să instanțieze obiecte locale pentru uz intern, care nu vor ieși NICIODATĂ din clasa respectivă, altfel ar fi trebuit să fie obiecte „has” normale.

În cele din urmă, pentru ca toate acestea să funcționeze, trebuie să aveți un design disciplinat, cu clase sub formă de ierarhie și fără cicluri.

În cadrul acestei concepții, ȘI în conformitate cu regulile de mai sus, nu există nicio posibilitate ca o clasă copil într-o concepție ierarhică să acceseze vreodată un pointer care a fost distrus, deoarece aceasta înseamnă că un părinte a fost distrus înaintea unui copil, ceea ce nu este permis de concepția ierarhică aciclică.

În cele din urmă, rețineți, de asemenea, că atunci când vă începeți sistemul trebuie să construiți de sus în jos în ierarhie și să distrugeți de jos în sus. Nu veți avea niciodată un pointer nul nicăieri, altfel cineva încalcă regulile.

Comentarii

  • De acord, deși numai conceptual. Da, ar trebui să ai un plan. Dar atunci ai nevoie de programatori care POT FACE PLANURI și care au capacitatea de a gândi logic și de a dezvolta și menține modele coerente. Și mai sunt și angajatori al căror obiectiv principal sunt banii și salariile ;-). Și apoi începeți să aruncați o privire la specificațiile JPA pentru a vă face o impresie despre ceea ce constituie un model/plan coerent și despre ceea ce este necesar pentru a-l dezvolta și/sau documenta. –  > Por user1050755.
  • Acest răspuns este relevant pentru C++, dar nu și pentru un limbaj cu garbage collected precum Java. –  > Por artbristol.
  • Cu tot respectul, nu sunt de acord. O bună gestionare a memoriei în JAVA ( sau aproape orice alt limbaj) vă va oferi multe beneficii. De la o stabilitate mai bună, deoarece se reduc punctele de nul, la o performanță mai bună, deoarece amprenta de memorie este mai mică, până la un design mai bun, deoarece relațiile sunt mai bine controlate. Voi sugera că gestionarea neglijentă a memoriei din multe aplicații JAVA duce la frecvente NPE-uri și scurgeri de memorie. –  > Por Alex Vaz.
  • Sunt întru totul de acord cu ceea ce ați scris și consider că este foarte regretabil faptul că Java nu face nicio distincție între referințele care încapsulează proprietatea și cele care nu o fac. Cei care ar argumenta că „pentru asta există GC” ratează un punct critic: GC evită nevoia de imuabile imuabile de a avea proprietari și, de asemenea, nu elimină posibilitatea ca o referință care atârnă la un obiect șters să se transforme într-o referință la un alt obiect arbitrar (așa cum se poate întâmpla în C++). Cu toate acestea, este greu de scris un cod corect dacă mai multe obiecte încapsulează starea unui obiect mutabil ca parte a sa proprie. –  > Por supercat.
  • Modul meu preferat de a gândi lucrurile este să spun că referințele pot încapsula identitatea, starea mutabilă, ambele sau niciuna. În plus, referințele pot încapsula o stare mutabilă fie pentru că identifică un obiect imuabil, fie pentru că obiectul pe care îl identifică nu va fi niciodată expus la ceva care l-ar putea muta. Este păcat că Java nu are un concept de astfel de distincții, deoarece lucruri precum verificarea egalității și clonarea ar putea fi gestionate 99% automat dacă ar avea un astfel de concept. –  > Por supercat.

În primul rând, nu putem elimina cu adevărat toate condițiile de nulitate. Le putem reduce folosind @NotNull și @Nullable adnotări (după cum s-a menționat deja). Dar acest lucru trebuie să fie susținut de un anumit cadru. În acest sens OVal poate fi de ajutor.

Ideea de bază este că obiectul/parametrii/constructorul ar trebui să satisfacă întotdeauna condițiile prealabile. Puteți avea o mulțime de precondiții, cum ar fi Nullable, NotNull iar OVal ar avea grijă ca un obiect să fie într-o stare coerentă atunci când este invocat.

Presupun că OVal utilizează în mod intern AspectJ pentru a valida precondițiile.

@Guarded
public class BusinessObject
{
  public BusinessObject(@NotNull String name)
  {
    this.name = name;
  }

  ...
}

De exemplu,

// Throws a ConstraintsViolatedException because parameter name is null
BusinessObject bo = new BusinessObject(null);

Prefer acest lucru

public void simpleFunc(SomeObject someObject){
    someObject = someObject != null ? someObject : new SomeObject(null);
    someObject.doSomething();
}

Desigur, în exemplul meu, SomeObject gestionează cu grație un parametru nul. De exemplu, înregistrează un astfel de eveniment și nu face nimic altceva.

Am folosit bibliotecile Apache (Apache Commons) pentru această problemă.

ObjectUtils.equals(object, null)

sau

CollectionUtils.isEmpty(myCollection);

sau

StringUtils.isEmpty("string");

Îmi place răspunsul anterior, ca o practică, de a oferi valori inițiale implicite sau seturi goale pentru colecții pentru a minimiza nevoia.

Acestea pot fi utilizări simple care vă împiedică să aveți NullPointerException sau să folosiți o colecție goală. Acest lucru nu răspunde la întrebarea ce trebuie să se facă cu obiectul nul, dar acestea oferă unele verificări pentru validările de bază ale obiectului sau colecției.

Sperăm că acest lucru vă ajută.

Comentarii

  • Nu-mi place CollectionUtils.isEmpty și StringUtils.isEmpty deoarece acestea efectuează, de asemenea, o verificare a nulității, ceea ce nu este implicat de numele metodei. –  > Por Steve Kuo.

Este posibil să se definească metode utilitare care gestionează verificările de nul imbricate într-un mod aproape frumos cu Java 8 lambdas.

void example() {
    Entry entry = new Entry();
    // This is the same as H-MANs solution 
    Person person = getNullsafe(entry, e -> e.getPerson());    
    // Get object in several steps
    String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
    // Call void methods
    doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
}

/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
    if (o1 != null) return f1.apply(o1);
    return null; 
}

public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
    return getNullsafe(getNullsafe(o0, f1), f2);
}

public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
    return getNullsafe(getNullsafe(o0, f1, f2), f3);
}


/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
    if (o1 != null) f1.accept(o1);
}

public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
    doNullsafe(getNullsafe(o0, f1), f2);
}

public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
    doNullsafe(getNullsafe(o0, f1, f2), f3);
}


class Entry {
    Person getPerson() { return null; }
}

class Person {
    Name getName() { return null; }
}

class Name {
    void nameIt() {}
    String getGivenName() { return null; }
}

(Acest răspuns a fost postat pentru prima dată aici.)

O opțiune pe care o aveți

  • Utilizați cadrul de verificare‘s @RequiresNonNull pe metode. de exemplu, veți obține acest lucru dacă apelați o metodă adnotată ca atare, cu un argument nul. Acesta va eșua în timpul compilării, chiar înainte ca codul să ruleze! deoarece în timpul rulării va fi NullPointerException.

    @RequiresNonNull(value = { "#1" })
    static void check( Boolean x) {
        if (x) System.out.println("true");
        else System.out.println("false");
    }
    
    public static void main(String[] args) {
    
    
        check(null);
    
    }
    

primește

[ERROR] found   : null
[ERROR] required: @Initialized @NonNull Boolean
[ERROR] -> [Help 1]

Există și alte metode, cum ar fi Use Java 8’s Optional, Guava Annotations, Null Object pattern etc. Nu contează atâta timp cât vă obțineți obiectivul de a evita !=null

Kotlin cu siguranță null este o alternativă elegantă, dar înseamnă o schimbare mai mare.

Există o modalitate bună de a verifica valoarea nulă din JDK.Este vorba de Optional.java care are o mare de metode pentru a rezolva aceste probleme. Cum ar fi următoarele:`

  • Returnează un {@cod Opțional} care descrie valoarea specificată, dacă nu este nulă,
    • în caz contrar, returnează un {@code Optional} gol.
    • @param clasa valorii
    • @param value valoarea care trebuie descrisă, eventual nulă
    • @return an {@code Optional} cu o valoare prezentă dacă valoarea specificată
    • nu este nulă, în caz contrar, un {@code Optional} gol.
public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}  `

`/

  • Returnează {@code true} dacă există o valoare prezentă, în caz contrar {@code false}.

    • @return {@code true} dacă există o valoare prezentă, altfel {@code false}*/public boolean isPresent() {return value != null;}

    /**

    • Dacă este prezentă o valoare, invocă consumatorul specificat cu valoarea respectivă,
    • în caz contrar, nu se face nimic.
    • @param consumator blocul de executat în cazul în care este prezentă o valoare
    • @throws NullPointerException în cazul în care valoarea este prezentă și {@code consumer} este
    • null*/public void ifPresent(Consumer<? super T> consumer) {if (value != null)consumer.accept(value);}`

Este foarte, foarte util pentru a ajuta javer.

Pentru clasele de utilitate, puteți verifica dacă parametrii nu sunt nuli.

În toate celelalte cazuri, este posibil să nu fie nevoie să o faceți. Folosiți încapsularea cât mai mult posibil, reducând astfel locurile în care vă simțiți tentați să verificați dacă este nul.

Puteți să vă cuplați clasa cu testarea unitară folosind un cadru precum JUnit.În acest fel, codul dvs. va fi curat (fără verificări inutile) și veți fi sigur că instanțele dvs. nu vor fi nule.

Acesta este un motiv bun (dintre multe altele) pentru a utiliza testarea unitară.

Abordarea funcțională vă poate ajuta să înfășurați verificările repetitive ale nulității și să executați un cod anonim ca în exemplul de mai jos.

    BiConsumer<Object, Consumer<Object>> consumeIfPresent  = (s,f) ->{
        if(s!=null) {
            f.accept(s);
        }
    };

    consumeIfPresent.accept(null, (s)-> System.out.println(s) );
    consumeIfPresent.accept("test", (s)-> System.out.println(s));

    BiFunction<Object, Function<Object,Object>,Object> executeIfPresent  = (a,b) ->{
        if(a!=null) {
            return b.apply(a);
        }
        return null;
    };
    executeIfPresent.apply(null, (s)-> {System.out.println(s);return s;} );
    executeIfPresent.apply("test", (s)-> {System.out.println(s);return s;} );