Anonieme lessen in Java

1. Inleiding

In deze zelfstudie bekijken we anonieme klassen in Java.

We zullen beschrijven hoe we deze kunnen declareren en er voorbeelden van kunnen maken. We zullen ook kort hun eigenschappen en beperkingen bespreken.

2. Anonieme klasseverklaring

Anonieme klassen zijn innerlijke klassen zonder naam. Omdat ze geen naam hebben, kunnen we ze niet gebruiken om instanties van anonieme klassen te maken. Als gevolg hiervan moeten we anonieme klassen declareren en instantiëren in een enkele uitdrukking op het punt van gebruik.

We kunnen een bestaande klasse uitbreiden of een interface implementeren.

2.1. Breid een klas uit

Wanneer we een anonieme klasse instantiëren uit een bestaande, gebruiken we de volgende syntaxis:

Tussen haakjes specificeren we de parameters die vereist zijn door de constructor van de klasse die we uitbreiden:

nieuw boek ("Design Patterns") {@Override public String description () {return "Famous GoF book."; }}

Als de constructor van de bovenliggende klasse geen argumenten accepteert, moeten we de haakjes natuurlijk leeg laten.

2.2. Implementeer een interface

We kunnen ook een anonieme klasse instantiëren vanuit een interface:

Uiteraard hebben Java's interfaces geen constructors, dus de haakjes blijven altijd leeg. Dit is de enige manier waarop we het zouden moeten doen om de methoden van de interface te implementeren:

new Runnable () {@Override public void run () {...}}

Zodra we een anonieme klasse hebben geïnstantieerd, kunnen we die instantie aan een variabele toewijzen om er later ergens naar te kunnen verwijzen.

We kunnen dit doen met behulp van de standaardsyntaxis voor Java-expressies:

Runnable action = new Runnable () {@Override public void run () {...}};

Zoals we al zeiden, een anonieme klassendeclaratie is een uitdrukking en moet daarom deel uitmaken van een verklaring. Dit verklaart waarom we een puntkomma aan het einde van de verklaring hebben geplaatst.

Het is duidelijk dat we kunnen voorkomen dat de instantie aan een variabele wordt toegewezen als we die instantie inline maken:

Lijstacties = nieuwe ArrayList (); actions.add (new Runnable () {@Override public void run () {...}});

We moeten deze syntaxis met de grootste zorg gebruiken, omdat deze gemakkelijk de leesbaarheid van de code kan verminderen, vooral bij de implementatie van het rennen() methode neemt veel ruimte in beslag.

3. Anonieme klasse-eigenschappen

Er zijn bepaalde bijzonderheden bij het gebruik van anonieme klassen met betrekking tot de gebruikelijke klassen op het hoogste niveau. Hier gaan we kort in op de meest praktische zaken. Voor de meest nauwkeurige en bijgewerkte informatie kunnen we altijd kijken naar de Java-taalspecificatie.

3.1. Constructor

De syntaxis van anonieme klassen staat ons niet toe om ze meerdere interfaces te laten implementeren. Tijdens de bouw, er kan precies één instantie van een anonieme klasse bestaan. Daarom kunnen ze nooit abstract zijn. Omdat ze geen naam hebben, kunnen we ze niet verlengen. Om dezelfde reden kunnen anonieme klassen niet expliciet constructors hebben gedeclareerd.

In feite vormt de afwezigheid van een constructor voor ons geen enkel probleem om de volgende redenen:

  1. we maken anonieme klasse-instanties op hetzelfde moment als we ze declareren
  2. vanuit anonieme klasseninstanties hebben we toegang tot lokale variabelen en kunnen we de leden van de klas insluiten

3.2. Statische leden

Anonieme klassen kunnen geen statische leden hebben, behalve degenen die constant zijn.

Dit compileert bijvoorbeeld niet:

new Runnable () {statische finale int x = 0; statische int y = 0; // compilatiefout! @Override public void run () {...}};

In plaats daarvan krijgen we de volgende foutmelding:

Het veld y kan niet statisch worden gedeclareerd in een niet-statisch innerlijk type, tenzij geïnitialiseerd met een constante expressie

3.3. Bereik van variabelen

Anonieme klassen leggen lokale variabelen vast die binnen het bereik vallen van het blok waarin we de klasse hebben gedeclareerd:

int count = 1; Runnable action = new Runnable () {@Override public void run () {System.out.println ("Runnable met vastgelegde variabelen:" + count); }}; 

Zoals we zien, zijn de lokale variabelen tellen en actie worden gedefinieerd in hetzelfde blok. Om deze reden hebben we toegang tot tellen vanuit de klasseverklaring.

Merk op dat om lokale variabelen te kunnen gebruiken, ze effectief definitief moeten zijn. Sinds JDK 8 is het niet meer nodig dat we variabelen declareren met het trefwoord laatste. Niettemin moeten die variabelen dat wel zijn laatste. Anders krijgen we een compilatiefout:

[FOUT] lokale variabelen waarnaar wordt verwezen vanuit een innerlijke klasse, moeten definitief of effectief definitief zijn

Om de compiler te laten beslissen dat een variabele in feite onveranderlijk is, in de code, zou er maar één plaats moeten zijn waar we een waarde aan toekennen. Mogelijk vinden we meer informatie over effectief definitieve variabelen in ons artikel "Waarom moeten lokale variabelen die in Lambdas worden gebruikt definitief of effectief definitief zijn?"

Laten we even vermelden dat, zoals elke innerlijke klasse, een anonieme klas heeft toegang tot alle leden van de bijbehorende klas.

4. Gebruiksscenario's voor anonieme klassen

Er kan een grote verscheidenheid aan toepassingen van anonieme klassen zijn. Laten we enkele mogelijke use-cases onderzoeken.

4.1. Klassenhiërarchie en inkapseling

We zouden innerlijke klassen moeten gebruiken in algemene gevallen en anonieme klassen in zeer specifieke gevallen om een ​​schonere hiërarchie van klassen in onze applicatie te bereiken. Wanneer we innerlijke klassen gebruiken, kunnen we een fijnere inkapseling bereiken van de gegevens van de omsluitende klasse. Als we de functionaliteit van de innerlijke klasse definiëren in een klasse op het hoogste niveau, dan zou de omsluitende klasse openbaar of pakket zichtbaarheid van enkele van haar leden. Er zijn natuurlijk situaties waarin het niet erg gewaardeerd of zelfs maar geaccepteerd wordt.

4.2. Schonere projectstructuur

We gebruiken meestal anonieme klassen wanneer we wijzigingen moeten aanbrengen on the fly de implementatie van methoden van sommige klassen. In dit geval kunnen we voorkomen dat we nieuwe *.Java bestanden naar het project om klassen op het hoogste niveau te definiëren. Dit is vooral het geval als die klasse op het hoogste niveau slechts één keer zou worden gebruikt.

4.3. UI-gebeurtenisluisteraars

In toepassingen met een grafische interface is het meest voorkomende gebruik van anonieme klassen het maken van verschillende gebeurtenislisteners. Bijvoorbeeld in het volgende fragment:

button.addActionListener (nieuwe ActionListener () {public void actionPerformed (ActionEvent e) {...}}

we maken een instantie van een anonieme klasse die de interface implementeert ActionListener. Haar actie uitgevoerd methode wordt geactiveerd wanneer een gebruiker op de knop klikt.

Sinds Java 8 lijken lambda-expressies echter een meer geprefereerde manier te zijn.

5. Algemeen beeld

Anonieme klassen die we hierboven hebben overwogen, zijn slechts een specifiek geval van geneste klassen. Over het algemeen, een geneste klasse is een klasse die wordt gedeclareerd in een andere klasse of interface:

Als we naar het diagram kijken, zien we dat anonieme klassen samen met lokaal en niet-statische leden vormen de zogenaamde innerlijke klassen. Samen met statisch lid klassen, ze vormen de geneste klassen.

6. Conclusie

In dit artikel hebben we verschillende aspecten van anonieme Java-klassen besproken. We hebben ook een algemene hiërarchie van geneste klassen beschreven.

Zoals altijd is de volledige code beschikbaar in onze GitHub-repository.