Jelenlegi hely

13.1. Belső osztályok

Ahhoz, hogy megértsük, mi a belső osztály és mire jó, tekintsük át újra a Stack osztályt. Tegyük fel, hogy ehhez az osztályhoz hozzá szeretnénk adni egy olyan tulajdonságot, amely lehetővé teszi egy másik osztály számára, hogy kilistázza a veremben lévő elemeket vagy tagokat java.util.Iterator interfész használatával. Ez az interfész három metódus deklarációt tartalmaz:

public boolean hasNext();
public Object next();
public void remove();

Az Iterator definiálja azt az interfészt, ami végigmegy egyszer az elemeken egy megadott sorrend szerint, amelyet a következőképpen adunk meg:

while (hasNext()) {
    next();
}

A Stack osztály önmaga nem tudja végrehajtani az Iterator interfészt, mert az Iterator interfész API-ja bizonyos korlátokat szab: két különböző objektum nem listázhatja kis egyszerre a veremben lévő elemeket. Ugyanis nincs lehetőség arra, hogy megtudjuk, ki hívja meg a következő metódust, és a listázást nem lehet újraindítani, mert az Iterator interfésznek nincsen olyan metódusa, amely támogatná ezt. Így a kilistázást csak egyszer lehet végrehajtani, mert az Iterator interfésznek nincs arra metódusa, hogy az elejéhez visszamenjen a bejárás. Ehelyett egy belső osztály van, amely elvégzi a verem számára ezt a munkát. A belső osztály hozzáférhet a verem elemeihez, és már csak azért is hozzá kell, hogy tudjon férni, mert a veremnek a publikus interfésze csak LIFO hozzáférést támogat. Itt lép be a képbe a belső osztály. Itt látható egy verem implementáció, ami definiál egy belső osztályt, amelyet StackIterator-nak hívunk, és kilistázza a verem elemeit.

public class Stack {
    private Object[] items;
    public Iterator iterator() {
        return new StackIterator();
    }
    class StackIterator implements Iterator {
        int currentItem = items.size() - 1;
        public boolean hasNext() {
            ...
        }
        public Object next() {
            ...
        }
        public void remove() {
            ...
        }
    }
}

Figyeljük meg, hogy a StackIterator osztály közvetlenül hivatkozik a veremben levő elemek példányainak változóira. A belső osztályokat elsősorban arra használjuk, hogy implementáljuk a segítő osztályokat, úgy, mint itt, ebben a példában is láthatjuk. Ha GUI eseményeket akarunk használni, akkor ismernünk kell a belső osztályok használatának a szabályait, mert az eseménykezelés mechanizmusa elég erőteljesen használja ezeket.

Deklarálhatunk egy belső osztályt anélkül, hogy nevet adnánk neki. Az ilyen osztályokat névtelen osztályoknak hívjuk. Itt látható még egy változata a Stack osztálynak, ebben az esetben névtelen osztályt használunk az Iterator számára.

public class Stack {
    private Object[] items;
    public Iterator iterator() {
        return new Iterator() {
            int currentItem = items.size() - 1;
            public boolean hasNext() {
                ...
            }
            public Object next() {
                ...
            }
            public void remove() {
                ...
            }
        }
    }
}

A névtelen osztályok nehézkessé tehetik a kód olvasását, ezért csak olyan osztályokhoz érdemes használnunk, amelyek nagyon kicsik (nem több mint egy-két metódus), és amelyek használata eléggé egyértelmű, mint például az eseménykezelő osztály.