A legtöbbet olyan osztályokat használunk, amelyek nem másik osztályba vagy interfészbe vannak beágyazva. Az ilyen osztályt legfelső szintű osztálynak nevezzük (
top level class).
A Java nyelv megengedi osztályok deklarálását más osztályokban is, ezeket beágyazott osztályoknak nevezzük (
nested class). Ezeknek több fajtája van.
Egy beágyazott osztály kétféle lehet: statikus vagy nem statikus. Az első fajtát egyszerűen csak statikus beágyazott osztálynak nevezzük (
static nested class), a második neve pedig belső osztály (
inner class). A beágyazott osztály tagja az őt tartalmazó osztálynak, azaz a nem statikus beágyazott osztályok közvetlenül elérik a tartalmazó osztály más tagjait is, a statikusak azonban nem. Míg egy normális, azaz felső szintű osztály csak public vagy package private lehet, addig a beágyazott osztályok mind a négy féle hozzáféréssel rendelkezhetnek: public, protected, private és package private.
Beágyazott osztályok használata
- logikailag összetartozó osztályok (ha egy osztályt csak egyetlen másik használ, a kettőt egy helyen lehet deklarálni és egyiket a másikba ágyazni)
- növeli az enkapszulációt (példa: van két osztályunk, az egyiknek el kell érnie a másik adattagjait, melyek egyébként privátként lennének deklarálva - ebben az esetben ha beágyazzuk az osztályt, az adattagok elérhetőek maradnak a beágyazott osztály számára, viszont nem lesznek láthatóak más osztályok számára)
- könnyebben fenntartható (olvasható) kód
Statikus beágyazott osztályok
Mint normális esetben, a statikus beágyazott osztály nem érheti el a nem statikus tagokat (csak ha példányreferencián keresztül teszi azt) és osztályreferencián keresztül hivatkozunk rá.
TopLevelClass.StaticNestedClass instance = new TopLevelClass.StaticNestedClass();
Belső osztályok
Egy belső osztály egy példánya a beágyazó osztály példányával együtt - azon belül - él, azaz maga nem hivatkozhat semmilyen statikus tagra, de bármilyen példányváltozóra igen.
TopLevelClass top = new TopLevelClass();
TopLevelClass.InnerClass myObject = top.new InnerClass();
Két speciális belső osztályról kell még beszélni, ezek a név nélküli (
anonymous classes) és lokális osztályok (
local classes).
Lokális osztályok
Deklarálhatunk egy belső osztályt egy metóduson belül is. Ezek a lokális (belső) osztályok.
- a lokális osztály csak azon a blokkon belül használható, ahol definiálták
- lokális osztály deklarálásakor nem használhatóak a public, protected, private, static kulcsszavak (ezek csak az osztály tagjainál használhatóak, lokális változó vagy osztály deklarálásakor nem)
- a fentiek miatt statikus tagot / metódust / osztályt sem tartalmazhatnak a lokális osztályok, csak konstansokat, ha az static final - ként van deklarálva
- a lokális osztály eléri a lokális változókat, metódus paramétereket, kivétel paramétereket is, de akkor és csak akkor, ha azok final - ként vannak deklarálva (Erre az a magyarázat, hogy mivel a lokális osztályok élettartama hosszabb lehet, mint magának a metódusnak a lefutási ideje, a fordító a lokális változókról egy másolatot hoz létre, amit a lokális osztály használni fog. A másolat és a lokális változó közötti azonosságot csak úgy garantálhatjuk, hogy a lokális változó final - ként van deklarálva.)
Név nélküli osztályok
Az előzőt megtehetjük úgy is, hogy nem adunk nevet az osztályunknak, az ilyeneket nevezzük anoním / név nélküli belső osztályoknak, ilyenkor az osztály példányosítása és definiálása egyszerre, egy helyen történik.
- mivel az anoním osztálynak nincs neve, konstruktora sem lehet; ha szükség van konstruktorra, akkor vagy lokális osztályt kell használni, vagy inicializáló blokkot, amit pont erre a helyzetre találtak ki
- hasonlóan a lokális osztályokhoz, nem használhatóak a public, protected, private, static kulcsszavak és statikus mezőket stb. sem lehet definiálni benne, kivéve ha az final is
- akkor ajánlott használni, ha az osztályt, amit megadunk, rögtön használjuk is vagy csak egyszer kell használni, esetleg az osztálynak nagyon rövid a megvalósítása.
Példa név nélküli osztály használatára
Vegyük pl. a java.io.File osztály list() metódusát, ami kilistázza a könyvtárban lévő fájlokat. Mielőtt azonban visszaadja a listát, egy szűrőn futtatja át azokat, amit nekünk kell megadni paraméterként. Amikor egy ilyen fájl szűrőt adunk meg, akkor gyakorlatilag egy
adapter osztályt definiálunk, amely olyan kódot definiál, amit egy másik objektum hív meg. Adapter osztályokat általában célszerű anoním osztályként megadni.
File myFile = new File("/src");
String[] listOfFiles = myFile.list(new FilenameFilter() {
public boolean accept(File f, String s) { return s.endsWith(".java"); }
});
Tagosztályok (member classes)
A tagosztály olyan nem statikus osztály,mely közvetlenül egy másik osztályban vagy interfészben van. A tagosztály neve nem lehet ugyanaz, mint a befoglaló osztály neve és nem tartalmazhat statikus változót / metódust / osztályt sem, kivéve, ha az
static final.