In het handboek1 gebruiken we als voorbeeld een vliegtuig met
een vast aantal plaatsen. Een array houdt er een aantal instanties van de klasse Passagier
bij.
Niet op elke index hoeft er naar een object verwezen te worden. Is er geen verwijzing naar een object, dan heb je een
lege plaats in het vliegtuig. Beschouw de array plaatsen
zoals hieronder afgebeeld.
Stel dat we alle namen willen tonen in het Terminalvenster van BlueJ. Een eerste idee is om dit via de for-each lus te doen. Een mogelijke implementatie is dan:
for(Passagier p : plaatsen)
{
p.print();
}
De lus lijkt het werk te gaan doen want je krijgt geen compilatiefout. Maar bij het uitvoeren van de
methode print()
krijg je volgende foutmelding:
p
wijst het eerste object aan, i.e. het object op index 0. In het
objectendiagram hierboven zien we een verwijzing naar het object dat de naam
"Dennis"
bijhoudt. In het Terminalvenster zie je dan de naam Dennis
verschijnen.p
naar het object op index 1. Alleen, daar is geen object. Toch wordt het statement
p.print()
uitgevoerd. Je probeert dus informatie te tonen van een object dat niet bestaat. Immers,
op index 1 wordt verwezen (in het Engels to point) naar null
. Vandaar de naam van de fout:
NullPointerException
.Alvorens je informatie vraag aan een object, moet je controleren of p
naar een object wijst. Dit doe
je als volgt:
for(Passagier p : plaatsen)
{
if(p != null)
{
p.print();
}
}
Dezelfde redenering kan je nu toepassen bij de for-lus:
for(int index = 0; index < plaatsen.length; index++)
{
if(plaatsen[index] != null)
{
plaatsen[index].print();
}
}
In plaats van te kijken of een variabele al dan niet naar null
verwijst, werk je met de vierkante
haken en de index. Voor elke volgende index wordt gecontroleerd of plaatsen[index]
niet naar null
wijst.
Geen null
betekent een verwijzing naar een object waarvan je de naam kan tonen in het Terminalvenster.
Waarschijnlijk ben je tijdens de oefeningen op volgend probleem gebotst:
Nemen we als voorbeeld het zoeken naar een passagier met een zekere naam, bijvoorbeeld "Scottie"
Zolang de naam van de passagier verschillend is van "Scottie"
wordt verder gezocht. De naam van de
passagier op index 0 is "Dennis"
. Er wordt verder gezocht. De naam van de passagier op index 1 wordt
getest. Alleen, daar vind je geen verwijzing naar een passagier. Gevolg: een mooie rode NullPointerException
.
while(index < plaatsen.length && ! plaatsen[index].getNaam().equals("Scottie"))
{
index++;
}
Dit probleem oplossen, is niet makkelijk. Vandaar dat we de programmeertaal even verlaten en het probleem in het Nederlands omschrijven. Ons objectendiagram geven we wat meer vorm. Dat helpt het denken vooruit.
Zolang de index geldig is, heb je twee gevallen waarbij verder zoeken vereist is.
plaatsen[index] == null
"Scottie"
.
plaatsen[index] != null && ! plaatsen[index].getNaam().equals("Scottie")
Plakken we alle puzzelstukje aan elkaar dan krijgen we volgende voorwaarde bij een geldig index:
plaatsen[index] == null || (plaatsen[index] != null && ! plaatsen[index].getNaam().equals("Scottie"))
Misschien denk je, wat een gigantische voorwaarde. Kan dat niet eenvoudiger? Jawel. Onderstaande voorwaarde is voldoende.
plaatsen[index] == null || ! plaatsen[index].getNaam().equals("Scottie")
Maar heb je dan geen probleem bij plaatsen[index].getNaam()
indien er op de gegeven index niet verwezen
wordt naar een object? Het antwoord is neen.
Is er op de gegeven index geen object te bespeuren, dan
evalueert het eerste deel van de voorwaarde tot true
. Wanneer het eerste deel van een samengestelde voorwaarde
bij de logische OF waar is, dan weet je dat de hele logische expressie waar wordt, ongeacht de uitkomst
van het tweede deel.
Logische OR
true OR true → true
true OR false → true
Ook Java is dan zo vriendelijk om het tweede deel niet meer te controleren. Is er op de gegeven index
geen verwijzing naar een object te bespeuren, dan wordt ! plaatsen[index].getNaam().equals("Scottie")
niet meer gecontroleerd. Dus ook geen NullPointerException
.
Deze manier van evalueren van samengestelde booleaanse expressies heet lazy evaluation. Waarom nog verder controleren als je het antwoord reeds kent? Het tegenovergestelde bestaat ook en heet strict evaluation. Dat bespreken we hier niet.
De meest elegante oplossing van ons zoekprobleem gaat dan als volgt:
while(index < plaatsen.length && (plaatsen[index] == null || ! plaatsen[index].getNaam().equals("Scottie")))
{
index++;
}