CATEGORII DOCUMENTE |
Clase incluse
Clase incluse
O clasa Java poate contine, ca membri ai clasei, alte clase numite clase incluse ("nested classes"). In cazul unei singure clase incluse, structura va arata astfel:
public class Outer
. . . // alti membri ai clasei Outer
}
Clasele incluse cu nume primesc de la compilator un nume compus din numele clasei exterioare, caracterul '$' si numele clasei interioare (Outer$Inner). Clasele care nu sunt incluse in alte clase se numesc clase de nivel superior ("top-level classes").
Clasa inclusa poate fi si o clasa statica, sau o clasa abstracta sau o interfata. Un exemplu de interes pentru definirea de noi clase dictionar este interfata Entry, inclusa in interfata Map, cu metode asociate unei perechi cheie-valoare, ambele de tip Object:
public interface Map
}
In exemplul urmator clasa inclusa "Entry" este folosita numai in definitia clasei "ArrayMap". Clasa inclusa "Entry"implementeaza interfata inclusa Map.Entry:
public class ArrayMap extends AbstractMap
public String toString()
public Object getKey()
public Object getValue()
public Object setValue (Object v)
public boolean equals (Object obj)
}
// alti membri ai clasei ArrayMap
private ArraySet entries ; // multime de perechi cheie-valoare
public ArrayMap (int n)
public Object put ( Object key, Object value)
public Set entrySet ()
}
Tipul Entry este folosit in mai multe metode ale clasei AbstractMap. Exemplu:
public Object get(Object key)
return null;
}
Clasa interioara statica ReverseComparator din clasa Collections, este folosita de metoda statica "reverseOrder" prin intermediul unei variabilei statice:
public class Collections
private static final Comparator REVERSE_ORDER = new ReverseComparator();
private static class ReverseComparator implements Comparator,Serializable
}
. . . // alte metode si clase incluse din clasa Collections
}
Clase interioare
O clasa inclusa nestatica este numita si clasa interioara ("inner class"), pentru ca fiecare obiect din clasa exterioara va contine un obiect din clasa interioara. Cuvantul "nested" se refera la relatia sintactica dintre clase: clasa inclusa este definita in cadrul definitiei clasei exterioare; cuvantul "inner" se refera la relatia dintre obiectele claselor incluse: un obiect al clasei exterioare contine in el un obiect al clasei incluse.
Un exemplu este o clasa iterator inclusa in clasa colectie pe care actioneaza. In acest fel clasa iterator are acces la variabile private ale colectiei, nu poate fi instantiata direct ci numai prin intermediul colectiei (nu poate exista obiect iterator fara o colectie), fiind ascunsa altor clase. Exemplu de iterator pe vector, clasa interioara:
public class Vector extends AbstractList implements List, Cloneable
// definirea clasei iterator pe vector (inclusa)
class VectorEnumeration implements Enumeration
public Object nextElement()
}
// . . . alte metode din clasa Vector
}
Clasa iterator se poate defini si ca o clasa top-level, daca primeste o referinta la clasa colectie (ca parametru in constructor) si foloseste metode publice ale colectiei:
class VectorEnum implements Enumeration
public boolean hasMoreElements()
public Object nextElement()
}
Un exemplu din Java Tutorial arata ca uneori iteratorul trebuie sa aiba acces la datele colectiei (private), nefiind suficiente metodele publice de acces la acestea. Este cazul unui iterator pe o stiva vector, care face o enumerare a elementelor stivei de la varf spre baza stivei, fara a scoate obiecte din stiva (fara a folosi o alta stiva).
In exemplul urmator o clasa pentru un vector ordonat ('SortedArray') contine o variabila comparator ('cmp'), care poate fi initializata de un constructor al clasei. Daca se foloseste constructorul fara argumente, atunci variabila comparator primeste o valoare implicita, ca referinta la un obiect comparator dintr-o clasa interioara. Clasa 'DefaultComp' nu mai trebuie definita de utilizatori si transmisa din afara, ea este utila numai clasei in care este definita (clasa exterioara 'SortedArray'):
public class SortedArray extends ArrayList
}
// alti membri ai clasei SortedArray
Comparator cmp=new DefaultComp(); // comparator implicit
public SortedArray ()
public SortedArray (Comparator comp)
public boolean add (Object obj)
public int indexOf (Object obj)
O alta forma de clasa interioara este o clasa definita intr-o metoda a clasei externe. Exemplu de clasa pentru un obiect comparator inclusa in functia de ordomare:
// ordonarea unui dictionar in ordinea valorilor din dictionar (nu a cheilor)
static void sortByValue (Map m)
}
Set eset = m.entrySet(); // multime de perechi cheie-valoare
ArrayList entries = new ArrayList(eset); // vector de perechi cheie-valoare
Collections.sort (entries, new VComp()); // ordonare vector
System.out.println (entries); // afisare perechi ordonate dupa valori
}
Motivele definirii de clase interioare pot fi diverse:
- Pentru clase de interes local : clasa interioara este necesara numai clasei exterioare.
- Pentru reducerea numarului de clase de nivel superior si deci a conflictelor intre nume de clase (ca urmare a unor instructiuni "import" pentru pachete in care se afla clase cu nume identice).
- Pentru a permite clasei exterioare accesul la membri private ai clasei interioare.
- Pentru a permite claselor interioare accesul la variabile ale clasei exterioare si deci o comunicare mai simpla intre clasele incluse (prin variabile externe lor).
- Pentru ca o clasa sa poata mosteni functii de la cateva clase (mostenire multipla).
O clasa interioara poate mosteni de la o implementare (nu de la o interfata) in mod independent de mostenirea clasei exterioare si de mostenirile altor clase incluse. Aceste mosteniri se pot combina in obiecte ale clasei exterioare, pentru care se pot folosi metode mostenite de toate clasele incluse. Exemplu:
class A }
class B }
class C }
class M extends C // clasa inclusa derivata din A
class AltB extends B // clasa inclusa derivata din B
void f1 () // metoda a clasei M
void f2 () // metoda a clasei M
}
class X
}
Pentru clasa M se pot apela functii mostenite de la clasele A, B si C la care se pot adauga si alte functii suplimentare.
Simplificarea comunicarii intre clase
In exemplul urmator metodele clasei exterioare "SimpleList" se pot referi direct la variabile din clasa inclusa "Node" (si nu prin intermediul unor metode publice).
public class SimpleList extends AbstractList
}
// variabile ale clasei SimpleList
private Node head; // inceput lista
private int n; // nr de noduri in lista
// functii membre ale clasei SimpleList
public SimpleList ()
public int size()
public Object get (int k)
public boolean add (Object el)
}
O clasa iterator poate lucra cu variabile ale clasei colectie pe care face enumerarea; accesul direct la aceste variabile este simplificat daca se include clasa iterator in clasa colectie. Instantierea clasei interioare se face prin metoda "iterator" din clasa exterioara:
public class SimpleList extends AbstractList
public boolean hasNext ()
public Object next()
public void remove ()
}
// metoda a clasei SimpleList
public Iterator iterator ()
// alti membri ai clasei exterioare: clase, variabile, metode }
Clasele interioare cu date comune
O clasa inclusa intr-o alta clasa este un membru al acelei clase si are acces la ceilalti membri ai clasei (variabile si metode), variabilele fiind componente ale instantei curente.
Includerea a doua clase A si B intr-o aceeasi clasa C permite simplificarea transmiterii de date intre clasele A si B prin variabile ale clasei C. O astfel de situatie apare in cazul a doua clase cu rol de producator si respectiv de consumator care-si transmit date printr-o coada de obiecte, pentru acoperirea diferentei dintre ritmul de producere si ritmul de consumare a mesajelor. Obiectul coada este folosit atat de obiectul producator cat si de obiectul consumator. Avem cel putin doua solutii pentru a permite accesul la obiectul comun coada:
- Transmiterea unei referinte la acest obiect la construirea obiectelor producator si consumator .
- Includerea claselor producator si consumator intr-o clasa gazda, alaturi de obiectul coada.
Urmeaza mai intai solutia cu clase de acelasi nivel:
class Producer
public void put(Object x)
}
class Consumer
public Object get()
}
// simulare procese producator-consumator
class Simulare
}
Pentru comparatie urmeaza solutia cu clase interioare. Clasele incluse au fost declarate statice pentru a putea fi instantiate din metoda statica "main".
class Simulare
}
static class Consumer
}
static Queue q ; // obiectul coada
static Producer p ; // proces producator
static Consumer c ; // proces consumator
static byte k=1;
// simulare procese producator-consumator
public static void main(String[] args)
}
}
Clase interioare anonime
In exemplele anterioare clasele incluse au fost definite explicit, folosind cuvantul class, dar se pot defini ad-hoc clase incluse anonime, pe baza unei alte clase sau a unei interfete (prin extindere sau prin implementare implicita, deoarece nu se folosesc cuvintele extends sau implements).
Uneori numele unei clase incluse apare o singura data, pentru a crea un singur obiect de acest tip. In plus, clasa inclusa implementeaza o interfata sau extinde o alta clasa si contine numai cateva metode scurte. Pentru astfel de situatii se admite definirea ad-hoc de clase anonime, printr-un bloc care urmeaza operatorului new cu un nume de interfata sau de clasa abstracta.
Sintaxa definirii unei clase anonime este urmatoarea:
new Interf ( ) ;
unde 'Interf' este un nume de interfata (sau de clasa abstracta sau neabstracta) din care este derivata (implicit) clasa inclusa anonima.
O astfel de clasa nu poate avea un constructor explicit si deci nu poate primi date la construirea unui obiect din clasa anonima.
O situatie tipica pentru folosirea unei clase anonime definita simultan cu crearea unui obiect de tipul respectiv este cea in care transmitem unei functii un obiect de un subtip al interfetei Comparator (adresa unei functii de comparare). Exemplu de sortare a unei liste de obiecte in ordine descrescatoare :
Collections.sort (list, new Comparator( )
}); // aici se termina definitia clasei si instructiunea
Alt exemplu de clasa comparator definita ca o clasa interioara anonima:
class SortedArray extends ArrayList
};
public SortedArray ()
public SortedArray (Comparator comp)
public int indexOf (Object obj)
// alte metode
}
Iata si o alta definitie a metodei "iterator" din clasa "SimpleList", in care clasa iterator pentru liste este anonima si este definita in expresia care urmeaza lui new.
public Iterator iterator () {
return new Iterator() {
// definirea clasei anonime iterator ca o clasa inclusa
private Node pos=head.next;
public boolean hasNext ()
public Object next()
public void remove ()
}; // sfarsit instructiune return new Iterator ( )
} // sfarsit metoda iterator
Prin definirea de clase anonime codul sursa devine mai compact iar definitia clasei apare chiar acolo unde este folosita, dar pot apare dificultati la intelegerea codului si erori de utilizare a acoladelor si parantezelor.
Intre clasa interioara si clasa exterioara exista un 'cuplaj' foarte strans; acest cuplaj poate fi un dezavantaj la restructurarea (refactorizarea) unei aplicatii, dar poate fi exploatat in definirea unor clase de biblioteca (care nu se mai modifica). Exemplu:
public abstract class AbstractMap implements Map
public Object next()
public void remove()
}; // aici se termina instr. return new Iterator .
}
public int size()
public boolean contains(Object v)
}; // aici se termina instr. values= new .
}
return values;
}
}
Probleme asociate claselor incluse
O clasa inclusa intr-un bloc poate folosi variabile locale din acel bloc, inclusiv argumente formale ale functiei definite prin blocul respectiv. Orice variabila sau parametru formal folosit intr-o clasa inclusa trebuie declarat cu atributul final, deoarece aceasta variabila este copiata in fiecare instanta a clasei incluse si toate aceste copii trebuie sa aiba aceeasi valoare (nemodificata in cursul executiei). Exemplul urmator este o functie similara functiei "iterator" dintr-o clasa colectie, dar itereaza pe un vector intrinsec de obiecte. In prima varianta a acestei functii se defineste o clasa cu nume interioara unui bloc si care foloseste un argument al functiei care contine definitia clasei.
// functie care produce un iterator pentru vectori intrinseci
public static Iterator arrayIterator (final Object a[] )
public Object next()
public void remove() // neimplementata
}
return new AI();
}
Clasa AI poate sa fie definita ca o clasa inclusa anonima deoarece acest nume nu este folosit in afara functiei "arrayIterator". O clasa definita intr-un bloc nu este membra a clasei ce contine acel bloc si nu este vizibila din afara blocului. Exemplu de clasa anonima definita intr-un bloc:
// iterator pentru vectori intrinseci
public static Iterator arrayIterator (final Object a[] )
public Object next()
public void remove()
}; // aici se termina instr. return
}
O clasa anonima nu poate avea un constructor explicit si poate fie sa extinda o alta clasa, fie sa implementeze o interfata (ca in exemplul anterior) cu aceeasi sintaxa. Un nume de interfata poate urma cuvantului cheie new numai la definirea unei clase anonime care implementeaza acea interfata, iar lista de argumente trebuie sa fie vida.
O clasa anonima nu poate simultan sa extinda o alta clasa si sa implementeze o interfata, sau sa implementeze mai multe interfete.
Numele claselor incluse anonime sunt generate de compilator prin adaugarea la numele clasei exterioare a caracterului '$'si a unui numar (a cata clasa inclusa este).
Variabilele locale din clasa exterioara sau din blocul exterior sunt copiate de compilator in campuri private ale clasei interioare, cu nume generate automat de compilator si transmise ca argumente constructorului clasei interioare. Pentru functia anterioara compilatorul Java genereaza un cod echivalent de forma urmatoare:
public static Iterator arrayIterator (final Object a[])
class C$1 implements Iterator
public boolean hasNext ()
public Object next()
public void remove()
}
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1256
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved