Clase Interne Java

Vreau ca acest articol sa fie cel mai complet articol in limba romana despre clase interne in Java. Prin urmare il voi imparti in doua postari, prima fiind o scurta introducere fundamentala si aici voi arata cel mai rapid mod de a scrie clase interne, iar a doua fiind o discutie avansata ce contine toate aspectele claselor interne, filosofia lor si tot ce se poate face cu ele. Cunostintele din acest articol se preteaza Java 5 (Tiger).

Pentru inceput, voi spune ca sunt patru tipuri de clase interne numite astfel:

  • clase interne
  • clase interne metodelor (clase locale metodelor)
  • clase interne anonime (sau simple clase anonime)
  • clase interne statice (imbricate) – tratate in partea a 2-a a articolului

In Java, toate acestea sunt clase interne.

Exista multe scopuri pe care le au si intrebuintari, dar folosirea lor in afara acestor recomandari este malefica sau nu tocmai indicata. Nevoia utilizarii unei clase interne apare atunci cand o chestiune de design apare: cum putem crea o clasa care sa fie "friend" (pentru cei ce stiu C++) clasei noastre? Desigur, o facem clasa interna, dar astfel aceasta clasa este intima clasei noastre si poate accesa pana si campurile sale private. Din exteriorul clasei externe, o clasa interna nu poate fi folosita (sau nu trebuie) decat daca avem o instanta a clasei externe.

O clasa interna se declara astfel:

class ExternalClass {
private int a;
class InternalClass {
public void access(){
System.out.println(a);
}
}
}

Simplu, nu? Tot din acest exemplu putem vedea ca InternalClass poate accesa orice membru al ExternalClass ca si cum ar fi al sau. Reciproc este de asemenea valabil.

O clasa interna nu poate fi initializata static, doar intr-o instanta a clasei externe, nu poate avea membri statici (pentru ca nu este legata de o instanta a celei externe) dar poate fi instantiata din exteriorul clasei externe prin urmatoarea forma:

ExternalClass.InnerClass clasa = new ExternalClass().new InnerClass();

Poate nu este foarte ciudat, dar in orice caz, este o constructie pe care nu mai intalnim nicaieri. Deci va rog, nu generalizati ci luati-o ca atare, este si asa suficient de intuitiva si autoexplicativa aceasta forma.

Pentru a accesa instanta unei clase interne din interiorul sau, se foloseste this asa cum v-ati fi asteptat, desi pentru a accesa un camp din clasa parinte il adresam precum ar fi in clasa interna. Dar exista o singura exceptie: pentru a accesa this-ul clasei externe, trebuie sa scriem ExternalClass.this. Adica sa prefixam this-ul cu numele clasei externe. Putin ciudat, stiu, din nou nu generalizati, acesta fiind unicul caz in care prefixam this-ul.

O clasa interna unei metode are vizibilitate doar in acea metoda, ca si cum ar fi o variabila locala… daca vreti putem spune ca este un tip local sau o clasa locala. In rest, se comporta la fel precum clasele interne obisnuite, cu doua exceptii: nu pot fi instantiate din exteriorul metodei continatoare (deci clar nici din exteriorul clasei externe) si nu se pot vedea variabilele locale din metoda continatoare decat daca sunt declarate final. Desigur, sintaxa este cum va si asteptati:

class ExternalClass {
private int a;
public void ContainerMethod(){
final int b;
class InternalClass {
public void access(){
System.out.println(a);
System.out.println(b);
}
}
InternalClass instance = new InternalClass();
Instance.access();
}
}
O clasa anonima, este mai degraba un obiect si o clasa in acelasi timp. Cele mai frecvent intalnite si desigur singura solutie pentru un mecanism de tratare al evenimentelor elegant sunt clasele anonime. Ele impreuna cu interfetele merg ca laptele cu mierea. Desigur, nu abuzati.

Sintaxa:

class ExternalClass {
public Object = new Object(){
//methods
};
}

O clasa anonima este mereu derivata din ceva, sau mai bine zis orice clasa in Java deriva din ceva iar daca nu explicit atunci deriva implicit din Object, la fel si clasa noastra doar ca in cazul claselor anonime trebuie sa scriem aceasta (trebuie sa respectam sintaxa). Deoarece o clasa anonima nu are nume nu putem crea obiecte de tipul sau (iarasi sintaxa crearii unui obiect necesita specificarea tipului) decat un singur obiect. Prin urmare, nu putem refolosi aceasta clasa anonima si acesta este poate si scopul. In cazul nostru creem un obiect de tip Object extins cu ce urmeaza intre {,}. Adica extindem tipul Object pe loc si creem si o instanta a tipului obtinut (adica tipul Object extins si nedenumit). Acea instanta insa este stocata intr-o referinta de tipul Object si singurul mod in care putem avea vreun avantaj din tipul extins este polimorfisuml. Adica sa modificam implementarea unei metode, dar nu sa adaugam membrii noi pentru ca niciodata nu i-am putea vedea (decat poate prin Reflection).

Prin urmare, o clasa anonima poate imlpementa o interfata (singurul motiv pentru care o interfata nu este instantiabila este pentru ca metodele sale sunt abstracte si deci sunt lipsite de implementare). Daca extindem o interfata printr-o clasa anonima si implementam in aceasta toate metodele abstracte, putem obtine un obiect si astfel vedem ca in loc de Object putem scrie orice clasa sau interfata calificata in acel loc.

Da, poate acum realizati ca interfetele si clasele anonime merg impreuna… in fine… stiti voi cum.

Aici se termina acest articol si va invit in continuare sa cititi partea a doua (cand o sa apara in cateva zile) privind aspectele mai avansate ale claselor interne.