Indhold:
Indre klasser, herunder lokale klasser og anonyme klasser
Brug af indre klasser og anonyme klasser til at lytte efter hændelser
Brug af anonyme klasser til at oprette bl.a. tråde i en håndevending
Kapitlet er en forudsætning for at forstå den måde, mange værktøjer laver kode til at håndtere hændelser.
Forudsætter kapitel 11, Interfaces, afsnittet om final i kapitel 20, Avancerede klasser (og 12, Hændelser og 16, Flertrådet programmering for at forstå nogle af eksemplerne).
Indre klasser er mindre "hjælpeklasser" defineret inde i en anden klasse. Dette kapitel handler om de forskellige måder at definere indre klasser på, og de forhold, der her gør sig gældende.
Siden Java version 1.1 har der eksisteret 3 slags indre klasser:
(Almindelige) indre klasser
Lokale klasser
Anonyme klasser
Der er flere fordele ved at benytte indre klasser (visse undtagelser bliver forklaret sidst i kapitlet):
Den indre klasse er knyttet til den ydre klasse og kan kun
anvendes i denne.
Man behøves derfor ikke bekymre sig for
sammenhængen med resten af programmet. Det kan give et mere
overskueligt program at lægge klasser, der alligevel har meget
stærk binding (er meget afhængige af hinanden) inden i
hinanden.
Den indre klasse kan arbejde direkte på den ydre
klasses variabler og metoder, også de private.
Det
skyldes, at et objekt af en indre klasse altid hører til et
objekt af den ydre klasse.
En almindelig indre klasse er en klasse, der erklæres på linie med objektvariabler og metoder:
public class YdreKlasse { class IndreKlasse { } }
Programkoden i den indre klasse kan anvende alle den ydre klasses variabler og metoder - også de private. Den indre klasse er knyttet til et objekt af den ydre klasse.
Man benytter ofte indre klasser i forbindelse med at lytte efter hændelser. Her kommer Linietegning-eksemplet fra kapitel 12 igen, men hvor vi lader en indre klasse lytte efter museklik.
import java.applet.*; import java.awt.*; import java.awt.event.*; public class LinietegningIndre extends Applet { // Selv private variabler er synlig for den indre klasse private Point trykpunkt = null; private Point slippunkt = null; public void init() { Linielytter lytter = new Linielytter(); this.addMouseListener(lytter); } // En indre klasse class Linielytter implements MouseListener { public void mousePressed (MouseEvent event) { trykpunkt = event.getPoint(); // sæt variablen i det ydre objekt } public void mouseReleased (MouseEvent event) { slippunkt = event.getPoint(); repaint(); // kald det ydre objekts metode } public void mouseClicked (MouseEvent event) {} // kræves af MouseListener public void mouseEntered (MouseEvent event) {} // kræves af MouseListener public void mouseExited (MouseEvent event) {} // kræves af MouseListener } // slut på indre klasse // en metode i den ydre klasse public void paint (Graphics g) { if (trykpunkt != null && slippunkt != null) g.drawLine (trykpunkt.x, trykpunkt.y, slippunkt.x, slippunkt.y); } }
Læg mærke til, at den indre klasse uden videre har adgang til den ydre klasses variabler og metoder.
En lokal klasse er defineret i en blok programkode ligesom en lokal variabel.
public class YdreKlasse
{
public void metode()
{
// ...
class LokalKlasse
{
// metoder og variabler her ...
}
LokalKlasse objektAfLokalKlasse = new LokalKlasse();
// ...
}
}
Lokale klasser er kun synlige og anvendelige i den blok, hvor de er defineret. Ligesom lokale variabler er de ikke synlige uden for metoden (og nøgleordene public, private, protected og static foran klassen har derfor ingen mening).
Lokale klasser kan benytte alle variabler og metoder, der er synlige inden for blokken. Dog skal lokale variabler i den omgivende metode være erklæret final, dvs. være konstante, før de kan bruges i den lokale klasse.
Lokale klasser bruges ret sjældent (men de er gode at forstå, før man går videre til anonyme klasser)
Nedenstående er et eksempel på en lokal klasse, der benytter variabler defineret i den ydre klasse:
public class YdreKlasseMedLokalKlasse
{
private int a1 = 1; // Objektvariabler behøver ikke være final
public void prøvLokaltObjekt(final int a2) // Bemærk: final
{
final int a3 = 3; // Bemærk: final
class LokalKlasse { // definér lokal klasse
int a4 = 4;
public void udskriv()
{
System.out.println( a4 );
System.out.println( a3 );
System.out.println( a2 );
System.out.println( a1 );
}
} // slut på lokal klasse
LokalKlasse lokal = new LokalKlasse(); // opret lokalt objekt fra klassen
lokal.udskriv();
}
public static void main(String args[]){
YdreKlasseMedLokalKlasse ydre = new YdreKlasseMedLokalKlasse();
ydre.prøvLokaltObjekt(2);
}
}
4 3 2 1
En anonym klasse er en klasse uden navn, som der oprettes et objekt ud fra der, hvor den defineres.
public class YdreKlasse { public void metode() { // ... programkode for metode() X objektAfAnonymKlasse = new X() { void metodeIAnonymKlasse() { // programkode } // flere metoder og variabler i anonym klasse }; // mere programkode for metode() } }
Lige efter new angives det, hvad den anonyme klasse arver fra, eller et interface, der implementeres (i dette tilfælde X).
Fordelen ved anonyme klasser er, at det tillades på en nem måde at definere et specialiseret objekt præcis, hvor det er nødvendigt - det kan være meget arbejdsbesparende.
Man kan ikke definere en konstruktør til en anonym klasse (den har altid standardkonstruktøren). Angiver man nogen parametre ved new X(), er det parametre til superklassens konstruktør.
Følgende program udskriver alle javafiler i det aktuelle katalog. Det sker ved at kalde list()-metoden på et File-objekt og give det et FilenameFilter-objekt som parameter.
Interfacet FilenameFilter har metoden accept(File dir, String filnavn), som afgører, om en fil skal tages med i listningen (se evt. Javadokumentationen).
import java.io.*; public class FilnavnfiltreringMedAnonymKlasse { public static void main(String arg[]) { File f = new File( "." ); // det aktuelle katalog FilenameFilter filter; filter = new FilenameFilter() { // En anonym klasse public boolean accept( File f, String s) // En metode { return s.endsWith( ".java"); // svar true hvis fil ender på .java } } // slut på klassen ; // slut på tildelingen filter = new ... // brug objektet som filter i en liste af et antal filer String[] list = f.list( filter ); for (int i=0; i<list.length; i=i+1) System.out.println( list[i] ); } }
YdreKlasseMedLokalKlasse.java FilnavnfiltreringMedAnonymKlasse.java LinietegningAnonym.java AnonymeTraade.java A.java BenytIndreKlasser.java
Udviklingsværktøjer benytter ofte anonyme klasser i forbindelse med at lytte efter hændelser. Her er Linietegning-eksemplet igen, hvor vi bruger en anonym klasse som lytter (sml. eksemplet i 21.1.1).
import java.applet.*; import java.awt.*; import java.awt.event.*; public class LinietegningAnonym extends Applet { private Point trykpunkt = null; private Point slippunkt = null; public void init() { this.addMouseListener( new MouseListener() { public void mousePressed (MouseEvent event) { trykpunkt = event.getPoint(); } public void mouseReleased (MouseEvent event) { slippunkt = event.getPoint(); repaint(); } public void mouseClicked (MouseEvent event) {} public void mouseEntered (MouseEvent event) {} public void mouseExited (MouseEvent event) {} } // slut på anonym klasse ); // slut på kald til addMouseListener() System.out.println("Anonymt lytter-objekt oprettet"); } public void paint (Graphics g) { if (trykpunkt != null && slippunkt != null) g.drawLine (trykpunkt.x, trykpunkt.y, slippunkt.x, slippunkt.y); } }
Her gennemløber vi en løkke, der i hvert gennemløb opretter et Runnable-objekt fra en anonym klasse og en tråd, der kører på det. Objekterne får hvert sit nummer fra 1 til 5, som de udskriver 20, gange før de slutter. For at få trådene til at kæmpe lidt om processortiden løber de i en anden løkke 1.000.000 gange mellem hver udskrivning.
public class AnonymeTraade
{
public static void main(String arg[])
{
for (int i=1; i<=5; i=i+1)
{
// n bruges i den anonyme klasse
final int n = i;
Runnable r = new Runnable()
{
public void run()
{
for (int j=0; j<20; j=j+1)
{
System.out.print(n);
// Lav noget der tager tid
int x = 0;
for (int k=0; k<1000000; k=k+1) x=x+k;
}
System.out.println("Færdig med "+n+".");
}
};
Thread t = new Thread(r);
t.start();
}
}
}
111122221223311332441114433221144332211442255115544332115544332211Færdig med 1. 54442233332233Færdig med 2. 544335544Færdig med 3. 54Færdig med 4. 555555555Færdig med 5.
Man ser, hvordan objekt nummer 1, der blev startet først, også er det første, der afslutter.
Dette afsnit findes i den trykte bog
Tag TegnbareObjekter.java fra kapitel 11 og lav (i init()-metoden) fem forskellige objekter, der implementerer Tegnbar-interfacet (brug anonyme klasser). De fem objekter skal have forskellig måde at reagere på tegn() og sætPosition().
Kig på javadokumentationen til interfacet Comparator i pakken java.util. Lav tre Comparator-objekter (vha. anonyme klasser), der sorterer strenge hhv. alfabetisk, omvendt alfabetisk og alfabetisk efter andet tegn i strengene. Lav en liste (Vector) med ti strenge, og test din sortering med Collections.sort(liste, Comparator-objekt).
Dette afsnit findes i den trykte bog