Final methods/classes een zegen of een vloek?
In het eerste deel ben ik ingegaan op het gebruik van final bij variabelen. In dit deel wil ik het gebruik van final bij functies en klassen bespreken.
Functies (methods)
Door het keyword final voor een functie te plaatsen geef je als programmeur aan dat deze functie, in een afgeleide class, niet overriden kan worden. Een sub-class kan van zo’n functie dus geen eigen implementatie hebben.
Maar wacht eens even, dat was nou toch zo mooi in talen die overerving en overriding toestaan. Dan kan in een sub-class toch net het gedrag gewijzigd worden in iets wat je wil? Dat is inderdaad waar, maar dat leidt soms tot heel moeilijk te vinden fouten. De programmeur van de base class kan namelijk in zijn overige code van een bepaald gedrag van de functie zijn uitgegaan.
Voorbeeld: In een functie wordt een berekening uitgevoerd waarbij een object variable geïnitialiseerd wordt, die door latere functies in die class gebruikt wordt. Als de overriding functie in de sub-class deze initialisatie niet uitvoert, kan deze andere functie nu fouten genereren.
In zo’n geval is het beter om het verplichte gedrag in een methode uit te voeren die niet overridden kan worden en het mogelijk te wijzigen gedrag uit te laten voeren door niet final (’hook’) functies.
In onderstaande voorbeeld wordt bijvoorbeeld voor de hook gezorgd dat er altijd een weapon is en tevens een person is gevonden. Wat er gedaan wordt in de hook functie en hoe de persoon wordt gevonden is daarentegen wel aanpasbaar.
public class CaptainHook { private final Hook hook; public CapatainHook(Hook hook) { this.hook = hook; } public final void hookPerson(String name) { hook.prepare(); // voordat peter pan ge-hookt kan worden moet // hij eerst gevonden worden. Person person = findPerson(name); if (person != null) { hook(peterPan); hook.clean(); } } protected void hook(Person person) { hook.useOn(person); } protected Person findPerson(String name) { ... } }
Zo’n constructie zie je bijvoorbeeld ook bij frameworks. Voorbeeld is een centrale servlet die het voor het framework noodzakelijke toestand van het HttpRequest afhaalt en daarna zijn werk delegeert aan een andere functie of zelfs class die het eigenlijke werk moet gaan verrichten met behulp van deze toestand.
Een ander voorbeeld is te vinden op http://en.wikipedia.org/wiki/Template_method_pattern. Hier wordt het template design pattern besproken wat ook gebruik maakt van final om het verplichte gedrag af te dwingen, maar de implementatie van het specifieke gedrag aan concrete sub-classes overlaat.
Maar wat nu als je net die klasse hebt gevonden die lijkt te doen wat jij wilt, maar net die ene functie die je wilde aanpassen is final gemaakt. Wat dan, vloeken? Nee dan kan je altijd nog compositie gebruiken. Je zal dan wel alle methodes die je wilde overerven hun werk moeten laten delegeren naar de ingesloten klasse. Gelukkig kunnen moderne IDEs deze code voor je genereren.
Performance
Doordat een final method niet via een lookup table hoeft aangeroepen te worden, de VM weet dat er niet meerdere versies van zijn, kan de VM zo’n methode ook sneller aanroepen. Net als bij final variables is dit echter geen argument meer voor de moderne VM’s. Deze zijn heel goed in staat om te bepalen of een methode statisch of dynamisch aangeroepen moet worden.
Klassen (classes)
Wat hierboven beschreven staat voor final methods geldt eigenlijk ook voor final classes, alleen mag je nu de hele klasse niet meer overerven. Dit wordt met name gedaan met klasse waarvan het gedrag 100% voorspelbaar moet zijn.
Denk hierbij aan andere klassen die heel erg afhankelijk zijn van het gedrag van zulke klassen. Neem bijvoorbeeld de java.lang.String class. De meeste programma’s zouden niet meer goed werken als String wel hun achterliggende char[] konden wijzigen. Een ander punt waar final klassen worden gebruikt is bij het java security framework. Binnen dit raamwerk is het van groot belang dat het gedrag niet gewijzigd kan worden.
Conclusie
Bij functies en klasse gebruik je final als je als programmeur / ontwerper er zeker van wilt zijn dat anderen het bedoelde gedrag niet kunnen veranderen. Zorg er bij functies wel voor dat het gedrag wat veranderd mag worden via ‘hook’ functies beschikbaar is.
Het keyword final is er niet voor om code sneller uit te voeren maar om code veiliger en minder foutgevoelig te maken.
—————————————————————————————
Meer weten over Java-specialist Finalist IT Group?



De variabele ‘peterPan’ komt nergens voor in de ‘hookPerson’ methode… dat zal ‘person’ moeten zijn…
Maareh, interessant voorbeeld die CaptainHook…. want als je de class niet final maakt kun je de ‘hookPerson’ in een subclass wel overloaden; denk aan ‘void hookPerson(Person person)’ en als je toch al bezig bent met final wil je dat dus eigenlijk waarschijnlijk niet toestaan!
Peter Maas - januari 8, 2008 21:46
Peter,
Maar zo’n functie is niet beschikbaar via het type CaptainHook. En ja, als je niet wilt dat je class overerft kan worden moet je de class final maken.
Ernst-Jan - januari 17, 2008 18:44
private finalist Hook hook;
ralph - januari 23, 2008 11:12