Vraag Vermijdend = nulverklaringen


ik gebruik object != null veel te vermijden NullPointerException.

Is hier een goed alternatief voor?

Bijvoorbeeld:

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

Dit vermijdt a NullPointerException, wanneer het onbekend is of het object is null of niet.

Merk op dat het geaccepteerde antwoord mogelijk niet actueel is, zie https://stackoverflow.com/a/2386013/12943 voor een meer recente aanpak.


3548


oorsprong


antwoorden:


Dit klinkt voor mij als een redelijk veel voorkomend probleem dat junior tot intermediaire ontwikkelaars op een bepaald moment het hoofd moeten bieden: ze weten de contracten waaraan ze deelnemen niet of vertrouwen ze niet en verdedigen defensief nullen. Bovendien zijn ze bij het schrijven van hun eigen code afhankelijk van het retourneren van nulls om iets aan te geven waardoor de beller moet controleren op nulwaarden.

Om dit op een andere manier te zeggen, zijn er twee gevallen waarin nulcontrole wordt uitgevoerd:

  1. Wanneer null een geldig antwoord is in termen van het contract; en

  2. Waar het geen geldige reactie is.

(2) is eenvoudig. Gebruik het assert verklaringen (beweringen) of falen toestaan ​​(bijvoorbeeld NullPointerException). Assertions zijn een zeer weinig gebruikte Java-functie die is toegevoegd in 1.4. De syntaxis is:

assert <condition>

of

assert <condition> : <object>

waar <condition> is een Booleaanse expressie en <object> is een object waarvan toString() methode uitvoer zal worden opgenomen in de fout.

Een assert statement gooit een Error (AssertionError) als de voorwaarde niet waar is. Standaard negeert Java beweringen. U kunt beweringen inschakelen door de optie door te geven -ea naar de JVM. U kunt beweringen voor individuele klassen en pakketten inschakelen en uitschakelen. Dit betekent dat u de code met de beweringen kunt valideren tijdens het ontwikkelen en testen en ze kunt uitschakelen in een productieomgeving, hoewel mijn tests hebben aangetoond dat er geen invloed is op de prestaties van beweringen.

Het gebruik van beweringen in dit geval is niet OK, omdat de code gewoon zal mislukken, wat zal gebeuren als u beweringen gebruikt. Het enige verschil is dat met beweringen het eerder, op een betekenisvollere manier en mogelijk met extra informatie kan gebeuren, wat u zou kunnen helpen erachter te komen waarom het gebeurde als u het niet verwachtte.

(1) is een beetje moeilijker. Als je geen controle hebt over de code die je belt, zit je vast. Als null een geldig antwoord is, moet u erop letten.

Als het echter de code is die u wel beheert (en dit is vaak het geval), dan is het een ander verhaal. Vermijd het gebruik van nullen als reactie. Met methoden die collecties retourneren, is het eenvoudig: lege verzamelingen (of arrays) in plaats van nulls vrijwel de hele tijd retourneren.

Met niet-verzamelingen kan het moeilijker zijn. Beschouw dit als een voorbeeld: als je deze interfaces hebt:

public interface Action {
  void doSomething();
}

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

waarbij Parser de invoer van onbewerkte gebruikers gebruikt en iets te doen vindt, bijvoorbeeld als u ergens een opdrachtregelinterface implementeert. Nu zou je het contract kunnen maken dat het null terugkeert als er geen passende actie is. Dat leidt tot de nulcontrole waar je het over hebt.

Een alternatieve oplossing is om nooit null te retourneren en in plaats daarvan de Null-objectpatroon:

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

Vergelijken:

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

naar

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

dat is een veel beter ontwerp omdat het leidt tot meer beknopte code.

Dat gezegd hebbende, misschien is het helemaal juist dat de findAction () -methode een uitzondering genereert met een zinvolle foutmelding - vooral in dit geval waarin u vertrouwt op gebruikersinvoer. Het zou veel beter zijn als de findAction-methode een uitzondering zou geven dan voor de aanroepmethode om op te blazen met een eenvoudige NullPointerException zonder uitleg.

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

Of als u denkt dat het try / catch-mechanisme te lelijk is, in plaats van Do Nothing, moet uw standaardactie feedback geven aan de gebruiker.

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

2389



Als u een Java IDE-like gebruikt (of van plan bent te gebruiken) JetBrains IntelliJ IDEA, Eclipse of Netbeans of een tool zoals findbugs, dan kun je annotaties gebruiken om dit probleem op te lossen.

Kortom, je hebt @Nullable en @NotNull.

U kunt gebruiken in de methode en parameters, zoals deze:

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

of

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

Het tweede voorbeeld compileert niet (in IntelliJ IDEA).

Wanneer u de eerste gebruikt helloWorld() functioneer in een ander stuk code:

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

Nu zal de IntelliJ IDEA-compiler u vertellen dat de controle nutteloos is, aangezien de helloWorld() functie zal niet terugkeren nullooit.

Parameter gebruiken

void someMethod(@NotNull someParameter) { }

als je iets schrijft als:

someMethod(null);

Dit zal niet compileren.

Laatste voorbeeld met @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Dit doen

iWantToDestroyEverything().something();

En je kunt er zeker van zijn dat dit niet zal gebeuren. :)

Het is een leuke manier om de compiler iets meer dan normaal te laten controleren en om uw contracten krachtiger te maken. Helaas wordt dit niet door alle compilers ondersteund.

In IntelliJ IDEA 10.5 en later hebben ze ondersteuning voor een ander toegevoegd @Nullable  @NotNull implementaties.

Zie blogpost Meer flexibel en configureerbaar @ Nullable / @ NotNull annotaties.


516



Als null-waarden niet zijn toegestaan

Als uw methode extern wordt aangeroepen, begin dan met zoiets als dit:

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

Dan, in de rest van die methode, zul je dat weten object is niet nul.

Als het een interne methode is (geen onderdeel van een API), documenteer dan dat het niet nul kan zijn en dat is het dan.

Voorbeeld:

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

Als uw methode de waarde echter gewoon doorgeeft en de volgende methode deze doorgeeft, kan dit problematisch worden. In dat geval wilt u misschien het argument als hierboven controleren.

Als null is toegestaan

Dit hangt er echt van af. Als ik merk dat ik vaak zoiets doe als dit:

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

Dus ik vertak en doe twee compleet verschillende dingen. Er is geen lelijk codefragment, omdat ik echt twee verschillende dingen moet doen, afhankelijk van de gegevens. Moet ik bijvoorbeeld werken aan de invoer of moet ik een goede standaardwaarde berekenen?


Het komt zelden voor dat ik het idioom gebruik "if (object != null && ...".

Het is misschien makkelijker om voorbeelden te geven, als je voorbeelden laat zien van waar je het idioom meestal gebruikt.


282



Wauw, ik heb er bijna een hekel aan om nog een antwoord toe te voegen als we 57 verschillende manieren hebben om het aan te bevelen NullObject pattern, maar ik denk dat sommige mensen die geïnteresseerd zijn in deze vraag misschien willen weten dat er een voorstel op tafel ligt om Java 7 toe te voegen "null-veilige behandeling"-een gestroomlijnde syntaxis voor if-not-equal-null-logica.

Het voorbeeld van Alex Miller ziet er als volgt uit:

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

De ?. betekent dat alleen de linker-ID wordt gedeformeerd als deze niet nul is, anders moet de rest van de uitdrukking als worden geëvalueerd null. Sommige mensen, zoals lid van de Java Posse Dick Wall en de kiezers bij Devoxx hou echt van dit voorstel, maar er is ook tegenstand, op grond van het feit dat het daadwerkelijk meer gebruik van zal aanmoedigen null als een schildwachtwaarde.


Bijwerken: Een officieel voorstel voor een nul-veilige operator in Java 7 is ingediend onder Project Coin. De syntaxis is een beetje anders dan het bovenstaande voorbeeld, maar het is dezelfde notie.


Bijwerken: Het nul-veilige operatorvoorstel kwam niet in Project Coin. U ziet deze syntaxis dus niet in Java 7.


215



Als ongedefinieerde waarden niet zijn toegestaan:

U kunt uw IDE configureren om u te waarschuwen voor mogelijke nul-dereferencing. Bijv. in Eclipse, zie Voorkeuren> Java> Compiler> Fouten / Waarschuwingen / Null-analyse.

Als ongedefinieerde waarden zijn toegestaan:

Als u een nieuwe API wilt definiëren waar ongedefinieerde waarden zinvol zijn, gebruik de Optiepatroon (kan bekend zijn uit functionele talen). Het heeft de volgende voordelen:

  • In de API staat expliciet vermeld of een invoer of uitvoer bestaat of niet.
  • De compiler dwingt je om de "undefined" -case af te handelen.
  • Optie is een monade, dus er is geen behoefte aan uitgebreide nulcontrole, gebruik gewoon map / foreach / getOrElse of een vergelijkbare combinator om de waarde veilig te gebruiken (voorbeeld).

Java 8 heeft een ingebouwde Optional klasse (aanbevolen); voor eerdere versies zijn er bijvoorbeeld bibliotheekalternatieven guava's Optional of FunctionalJava's Option. Maar net als veel functionele stijlpatronen resulteert het gebruik van Option in Java (zelfs 8) in behoorlijk wat boilerplate, die u kunt verminderen met een minder uitgebreide JVM-taal, bijvoorbeeld Scala of Xtend.

Als u te maken krijgt met een API die mogelijk nulls retourneert, je kunt niet veel doen op Java. Xtend en Groovy hebben de Elvis-operator  ?: en de null-safe dereference operator  ?., maar merk op dat dit null retourneert in het geval van een nulreferentie, dus het "verslaat" alleen de juiste afhandeling van null.


177



Alleen voor deze situatie -

Niet controleren of een variabele nul is voordat een gelijkwaardige methode wordt aangeroepen (een tekenreeksvergelijkingsvoorbeeld hieronder):

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

zal resulteren in een NullPointerException als foo bestaat niet

U kunt dat vermijden als u uw vergelijkt Strings zoals dit:

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

160



Met Java 8 komt het nieuwe java.util.Optional klasse die op aantoonbare wijze een deel van het probleem oplost. Men kan op zijn minst zeggen dat het de leesbaarheid van de code verbetert, en in het geval van openbare API's maakt het contract van de API duidelijker voor de klantontwikkelaar.

Ze werken zo:

Een optioneel object voor een bepaald type (Fruit) is gemaakt als retourtype van een methode. Het kan leeg zijn of een bevatten Fruit voorwerp:

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

Kijk nu naar deze code waar we een lijst van zoeken Fruit (fruits) voor een bepaalde instantie Fruit:

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

U kunt de map() operator om een ​​berekening uit te voeren op - of een waarde te extraheren uit - een optioneel object. orElse() laat je een terugval bieden voor ontbrekende waarden.

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

Natuurlijk is de controle op nul / lege waarde nog steeds noodzakelijk, maar de ontwikkelaar is er zich ten minste van bewust dat de waarde mogelijk leeg is en dat het risico van vergeten te controleren beperkt is.

In een API die vanaf nul is opgebouwd met Optional wanneer een geretourneerde waarde mogelijk leeg is en een gewoon object alleen wordt geretourneerd als dit niet het geval kan zijn null (conventie), kan de clientcode nulcontroles op eenvoudige objectretourwaardewaarden opgeven ...

Natuurlijk Optional kan ook worden gebruikt als argument voor de methode, misschien een betere manier om optionele argumenten aan te geven dan 5 of 10 overbelastingsmethoden in sommige gevallen.

Optional biedt andere handige methoden, zoals orElse die het gebruik van een standaardwaarde toestaan, en ifPresent dat werkt met lambda-expressies.

Ik nodig u uit om dit artikel (mijn belangrijkste bron voor het schrijven van dit antwoord) te lezen waarin het NullPointerException (en in het algemeen null pointer) problematisch evenals de (gedeeltelijke) oplossing gebracht door Optional zijn goed uitgelegd: Java Optionele objecten.


135