User Tools

Site Tools


Problem constructing authldap
laboratoare:clase-interne
Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
laboratoare:clase-interne [2018/08/17 15:14]
Adriana Draghici [Clase interne]
laboratoare:clase-interne [2019/10/31 18:44] (current)
Adriana Draghici
Line 6: Line 6:
 == Obiective == == Obiective ==
  
- 
-Scopul acestui laborator este prezentarea conceptului de clasă internă și modalitățile de creare și folosire a claselor interne în Java. 
- 
-Aspectele urmărite sunt: 
 * prezentarea tipurilor de clase interne * prezentarea tipurilor de clase interne
-diferențele dintre clase interne ​statice și cele ne-statice +utilizarea claselor ​interne 
-utilitatea ​claselor interne+utilizarea sintaxei specifice ​claselor interne ​
  
  
Line 18: Line 14:
 ==Introducere== ==Introducere==
  
-Clasele declarate în interiorul unei alte clase se numesc clase interne (//nested classes//​) ​și reprezintă o funcționalitate importantă deoarece ​permit gruparea claselor care sunt legate logic și controlul vizibilității uneia din cadrul celorlalte. ​+Clasele declarate în interiorul unei alte clase se numesc clase interne (//nested classes//). Acestea ​permit gruparea claselor care sunt legate logic și controlul vizibilității uneia din cadrul celorlalte. ​
  
-Clasele interne sunt de mai multe tipuri, în funcție de modul de a le instanția și de relația lor cu clasa exterioră:+Clasele interne sunt de mai multe tipuri:
 * clase interne normale (//regular inner classes//) * clase interne normale (//regular inner classes//)
-* clase interne statice (//static nested classes//) 
 * clase anonime (//​anonymous inner classes//) * clase anonime (//​anonymous inner classes//)
 +* clase interne statice (//static nested classes//)
 * clase interne metodelor (//​method-local inner classes//) sau blocurilor * clase interne metodelor (//​method-local inner classes//) sau blocurilor
  
-<note important>​Unul din avantajele claselor interne este comportamentul acestora ​ca un **membru** al clasei. Asta face ca o clasa internă ​sa poata avea acces la toți membrii clasei ​de care aparține (//outer class//), inclusiv cei ''​private''​. În plus, aceasta poate avea modificatorii permiși metodelor și variabilelelor claselor. Astfel, o clasa internă poate fi nu numai ''​public'',​ ''​final'',​ ''​abstract''​ dar și ''​private'',​ ''​protected''​ și ''​static''​.</​note> ​+<note important>​ 
 +* O clasă internă se comportă ​ca un membru al clasei ​în care a fost declarată 
 +* O clasă ​internă ​are acces la toți membrii clasei ​în care a fost declarată, inclusiv cei **private** 
 +* O clasă ​internă poate fi ''​public'',​ ''​final'',​ ''​abstract''​ dar și ''​private'',​ ''​protected''​ și ''​static''​, însumând modificatorii claselor obișnuite și cei permiși metodelor și variabilelor  
 +* [[https://​docs.oracle.com/​javase/​tutorial/​java/​javaOO/​nested.html|Nested classes vsInner classes]] 
 +</​note>​ 
 ==Clase interne "​normale"​ == ==Clase interne "​normale"​ ==
  
Line 32: Line 34:
  
 <code java Test.java>​ <code java Test.java>​
-class Outer +interface Engine { 
-    class Inner +    public int getFuelCapacity();​ 
-        private int i;+
 + 
 +class Car 
 +    class OttoEngine implements Engine ​
 +        private int fuelCapacity;
  
-        public ​Inner(int i) { +        public ​OttoEngine(int fuelCapacity) { 
-            this.i;+            this.fuelCapacity ​fuelCapacity;
         }         }
  
-        public int value() { +        public int getFuelCapacity() { 
-            return ​i;+            return ​fuelCapacity;
         }         }
     }     }
  
-    public ​Inner getInnerInstance() { +    public ​OttoEngine getEngine() { 
-        ​Inner in = new Inner(11); +        ​OttoEngine engine ​= new OttoEngine(11); 
-        return ​in;+        return ​engine;
     }     }
 } }
Line 53: Line 59:
 public class Test { public class Test {
     public static void main(String[] args) {     public static void main(String[] args) {
-        ​Outer out = new Outer();+        ​Car car = new Car();
  
-        ​Outer.Inner in1 out.getInnerInstance(); +        ​Car.OttoEngine firstEngine ​car.getEngine(); 
-        ​Outer.Inner in2 out.new Inner(10);+        ​Car.OttoEngine secondEngine ​car.new OttoEngine(10);
  
-        System.out.println(in1.value()); +        System.out.println(firstEngine.getFuelCapacity()); 
-        System.out.println(in2.value());+        System.out.println(secondEngine.getFuelCapacity());
     }     }
 } }
 </​code>​ </​code>​
  
-În exemplul de mai sus, o dată ce avem o instanță a clasei Outer, sunt folosite două modalități de a obține o instanță a clasei ''​Inner''​ (definită în interiorul clasei ''​Outer''​):​ +<code html> 
-* definim o metodă ''​getInnerInstance'',​ care creează și întoarce o astfel de instanță;​ +student@poo:​~$ javac Test.java 
-* instanțiem efectiv ''​Inner'';​ observați cu atentie sintaxa folosita! Pentru a instanția ''​Inner'',​ avem nevoie de o instanta ''​Outer'':​ ''​out.new Inner(10);''​+student@poo:​~$ ls 
 +Car.class Car$OttoEngine.class Engine.class Test.class Test.java 
 +</​code>​ 
 + 
 +Urmăriți ​exemplul ​de folosire a claselor interne ​de mai sus. Adresați-vă asistentului pentru eventuale neclarități.
  
-Dintr-o clasă internă putem accesa **referința la clasa externă** (în cazul nostru ''​Outer''​) folosind numele acesteia și keyword-ul //this//:+Dintr-o clasă internă putem accesa **referința la clasa externă** (în cazul nostru ''​Car''​) folosind numele acesteia și keyword-ul //this//:
  
 <code java> <code java>
-Outer.this;+Car.this;
 </​code>​ </​code>​
  
-<​note ​important>Atenție ! Deșdintr-o clasă internă putem accesa membrii clasei externe, nu îi putem modifica ! (Hint: Pass by value) Pentru o explicație detaliată citiți [[ http://​techtracer.com/​2008/​04/​14/​mystery-of-accessibility-in-local-inner-classes/​ | Link1 ]] și [[ http://​stackoverflow.com/​questions/​1945663/​where-are-java-final-local-variables-stored | Link2]] ​.</​note>​+<​note ​hint>Rulați codul din exemplu. </​note>​
  
 ===Modificatorii de acces pentru clase interne=== ===Modificatorii de acces pentru clase interne===
Line 80: Line 90:
 Așa cum s-a menționat și în secțiunea [[.:​clase-interne#​introducere|Introducere]],​ claselor interne le pot fi asociați **orice** identificatori de acces, spre deosebire de clasele ''​top-level''​ Java, care pot fi doar ''​public''​ sau ''​package-private''​. Ca urmare, clasele interne pot fi, în plus, ''​private''​ și ''​protected'',​ aceasta fiind o modalitate de a **ascunde implementarea**. ​ Așa cum s-a menționat și în secțiunea [[.:​clase-interne#​introducere|Introducere]],​ claselor interne le pot fi asociați **orice** identificatori de acces, spre deosebire de clasele ''​top-level''​ Java, care pot fi doar ''​public''​ sau ''​package-private''​. Ca urmare, clasele interne pot fi, în plus, ''​private''​ și ''​protected'',​ aceasta fiind o modalitate de a **ascunde implementarea**. ​
  
-<code java Test.java> +Folosind exemplul anterior, modificați clasa ''​OttoEngine''​ pentru a fi privată. Observați erorile de compilare care rezultă.
-interface Hidden { +
-    public int value(); +
-}+
  
-class Outer { +* Tipul ''​Car.OttoEngine''​ nu mai poate fi accesat din exterior. Acest neajuns poate fi rezolvat cu ajutorul interfeței ''​Engine''​. Asociindu-clasei interne ''​Car.OttoEngine''​ supertipul ''​Engine''​ prin moștenire, putem instanția clasa prin **upcasting**.
-    private class HiddenInner implements Hidden { +
-        private int i;+
  
-        public HiddenInner(int ​i) { +* Fiind privată, clasa internă are implicit toți constructorii privați. Totuși, putem instanția obiecte de tipul ''​Car.OttoEngine''​ în interiorul clasei ''​Car'',​ urmând să le întoarcem folosind tot upcasting la ''​Engine''​. Astfel, folosind metode getEngine, ascundem complet implementarea clasei ''​Car.OttoEngine''​.
-            this.i = i; +
-        }+
  
-        public int value() { 
-            return i; 
-        } 
-    } 
  
-    public Hidden getInnerInstance() { 
-        HiddenInner in = new HiddenInner(11);​ 
-        return in; 
-    } 
-} 
  
-public class Test { 
-    public static void main(String[] args) { 
-        Outer out = new Outer(); 
  
-        Outer.HiddenInner in1 = out.getInnerInstance(); ​ // va genera eroare, deoarece 
-                                                        // tipul Outer.HiddenInner nu este vizibil 
-        Outer.HiddenInner in2 = new Outer().new HiddenInner(10);​ // din nou eroare 
  
-        Hidden in3 = out.getInnerInstance(); ​          // acces corect la o instanta HiddenInner 
-        System.out.println(in3.value());​ 
-    } 
-} 
-</​code>​ 
  
-Observați definirea interfeței ''​Hidden''​. Ea este utilă pentru a putea **asocia** clasei ''​HiddenInner''​ un ''​tip''​care să ne permita folosirea instanțelor acesteiaaltfel tipul ei nu ar fi fost vizibil pentru ca a fost declarată ''​private''​. ​ Observați, de asemenea, încercarile eronate ​de a instanția ''​HiddenInner''​Cum clasa internă a fost declarată ''​private''​acest tip nu mai este vizibil in exteriorul clasei ''​Outer''​.+==Clase anonime== 
 +În dezvoltarea software, există situații când o componentă a aplicației are o utilitate suficient de mare pentru a putea fi considerată o entitate separată (sau clasă). De multe oriînsă, aceasta ​nu este utilizată decât într-o porțiune restrânsă din aplicațieîntr-un context foarte specific (într-un lanț de moșteniri sau ierarhie ​de interfețe)Într-o aplicație reală, crearea unei clase pentru fiecare astfel de componentă poate duce la fenomenul de Class Explosioncare, pe scurt, ar aduce după sine o aplicație cu performanțe scăzute și greu de modificat/​extins.
  
 +Pentru a evita acest fenomen, putem folosi **clase interne anonime** în locul definirii unei clase cu număr de utilizări reduse. Acestea nu au nume și apar în program ca instanțe ale unei clase moștenite (sau a unei interfețe extinse), care suprascriu (sau implementează) anumite metode.
  
-==Clase interne în metode și blocuri==+Întorcându-ne la exemplul cu clasa top-level ''​Car'',​ putem rescrie metoda ''​getEngine()''​ a acesteia astfel:
  
-Primele exemple prezintă modalitățile cele mai uzuale de folosire a claselor interneTotuși, design-ul claselor interne este destul de complex și exista modalitati mai "​obscure"​ de a le folosi: clasele interne pot fi definite și în cadrul metodelor sau al unor blocuri arbitrare de cod+<code java> 
 +[...] 
 +class Car { 
 +    public Engine getEngine(int fuelCapacity) { 
 +        return new Engine () { 
 +            private int fuelCapacity = 11;
  
 +            public int getFuelCapacity() {
 +                return fuelCapacity;​
 +            }
 +        };
 +    }
 +}
 +[...]
 +</​code>​
  
-=== Clase interne în metode === +<​note>​ Modificați implementarea clasei Car. Rulați codul. Urmați instrucțiunile de mai jos pentru a restabilit funcționalitatea programului. Adresați-vă asistentului pentru neclarități.</​note>​
  
-În exemplul următor, clasa internă a fost declarată în **interiorul funcției** ​''​getInnerInstance''​. În acest mod, vizibilitatea ei a fost redusă pentru ca nu poate fi instanțiată decât în această funcție+Metoda folosită mai sus elimină necesitatea creări unei clase interne "​normale",​ reducând volumul codului și crescând lizibilitatea acestuia. Sintaxa ​''​return new Engine() { ... }'' ​se poate citi astfel: "Crează o clasă care implementează interfața Engine, conform următoarei implementări"​.
  
-Singurii modificatori care pot fi aplicați acestor clase sunt ''​abstract'' ​și'' ​final''​ (binențelesnu amândoi deodată).+Observații: 
 +* acest obiect este instanțiat imediat după ''​return''​, folosind ​''​new''​ (referința întoarsă de ''​new''​ va fi ''​upcast''​ la clasa de bază: ''​Engine''​) 
 +* numele clasei instanțiate este absent (ea este anonimă)însă ea este de ''​tipul''​ ''​Engine'',​ prin urmare, va implementa metoda/​metodele din interfață(cum e metoda ''​getFuelCapacity''​). Corpul clasei urmeaza imediat instanțierii.
  
-<code java Test.java> +<note tip>​IntelliJ sugerează înlocuirea cu funcții lambda însă acest concept nu este acoperit în laboratorPentru detalii suplimentare urmăriți acest [[https://​www.oracle.com/​webfolder/​technetwork/​tutorials/​obe/​java/​Lambda-QuickStart/​index.html#​section2|exemplu]]</​note
-interface Hidden { +  
-    ​public int value()+<note important>​O clasă internă anonimă poate extinde o clasă //sau// să implementeze o singură interfață,​ nu poate face pe ambele împreună ca la clasele ne-anonime ​(interne sau nu), și nici nu poate să implementeze mai multe interfețe. </​note>​
-}+
  
-class Outer { +=== Constructori === 
-    ​public Hidden getInnerInstance() + Clasele anonime **nu** pot avea **constructori** din cauză că nu au nume (nu am ști cum să numim constructorii). Această restricție asupra claselor anonime ridică o problemă: în mod implicit, clasă de bază este creată cu constructorul //​default//​. ​
-        class FuncInner implements Hidden { +
-            private int i = 11;+
  
-            public int value() { +Ce se întâmplă dacă dorim să invocăm un **alt constructor** al clasei de bază? În clasele normale acest lucru era posibil prin apelarea explicită, în prima linie din constructor a constructorului clasei de bază cu parametrii doriți, folosind ''​super''​. În clasele interne acest lucru se obține prin transmiterea parametrilor către constructorul clasei de bază **direct** la crearea obiectului de tip clasă anonimă: ​
-                return ​i+
-            } +
-        }+
  
-        return ​new FuncInner()+<code java> 
-    } +new Engine("​Otto"​) { 
-+    // ...
- +
-public class Test +
-    ​public static void main(String[] args) { +
-        Outer out = new Outer(); +
- +
-        Outer.FuncInner in2 = out.getInnerInstance();​ +
-        ​// EROARE: clasa FuncInner nu este vizibila +
-         +
-        Hidden in3 = out.getInnerInstance();​ +
- +
-        System.out.println(in3.value());​ +
-    }+
 } }
 </​code>​ </​code>​
  
 +În acest exemplu, am instanțiat o clasa anonimă, ce implementează interfața ''​Engine'',​ apelând constructorul clasei de bază cu parametrul ''"​Otto"''​.
  
-<note important>​Clasele interne declarate în metode nu pot folosi variabilele declarate în metoda respectivă și nici parametrii metodei. Pentru a le putea accesa, variabilele trebuie declarate ''​final'',​ ca în exemplul următor. Această restricție se datorează faptului că variabilele si parametrii metodelor se află pe segmentul de stivă (zonă de memorie) creat pentru metoda respectivă,​ ceea ce face ca ele să nu existe la fel de mult cât clasa internă. Dacă variabila este declarată ''​final'',​ atunci la runtime se va stoca o copie a acesteia ca un câmp al clasei interne, în acest mod putând fi accesată și după execuția metodei. 
-</​note>​ 
-<code java> 
-public void f() { 
-    final Student s = new Student(); 
-    // s trebuie declarat final ca sa poata fi accesat din AlterStudent 
  
-    class AlterStudent { +==Clase interne statice==
-        public void alterStudent() { +
-            s.name ​...               // OK +
-            s new Student(); ​        // GRESIT! +
-        } +
-    } +
-+
-</​code>​+
  
-=== Clase interne în blocuri ===  +În secțiunile precedente, s-a discutat doar despre clase interne ​ale caror instanțe există doar în contextul unei instanțe a clasei exterioare, astfel că poate accesa membrii obiectului exterior direct. De asemenea, am menționat că fiind membri ai claselor exterioare, clasele interne pot avea modificatorii disponibili pentru metode și variabile, dintre care și ''​static''​ (clasele exterioare nu pot fi statice!). Așa cum pentru a accesa metodele și variabilele statice ale unei clase nu este nevoie ​de o instanță a aceteia, putem obține o referință către o clasă ​internă ​fără a avea nevoie de o instanță a clasei exterioare. ​
-Exemplu ​de clasa internă ​declarata într-un **bloc**:+
  
-<code java> +Pentru a înțelege diferența dintre clasele interne statice și cele nestatice trebuie să reținem următorul aspect: **clasele nestatice țin legătura cu obiectul exterior** în vreme ce **clasele statice nu păstrează această legătură**. ​ 
-interface Hidden { +   
-    ​public int value()+Pentru clasele interne statice: ​  
-}+ * nu avem nevoie de un obiect al clasei externe pentru a crea un obiect al clasei interne ​  
 + * nu putem accesa câmpuri nestatice ale clasei externe din clasă internă ​(nu avem o instanță a clasei externe)
  
-class Outer { 
-    public Hidden getInnerInstance(int i) { 
-        if (i == 11) { 
-            class BlockInner implements Hidden { 
-                private int i = 11; 
  
-                public int value() { +<​note>​ 
-                    ​return ​i+* Pornind de la codul de [[.:​clase-interne#​Clase interne "​normale"​|aici]],​ modificați clasa internă ''​OttoEngine''​ pentru a fi statică. 
-                } +* Observați că trebuie modificat felul prin care aceasta este instanțiată. Pentru clasele interne statice, apelăm ''​new Car.OttoEngine()''​. 
-            }+* Rulațcodul. Observați că în interiorul clasei ''​Car'',​ putem instanția clasa internă statică folosind doar ''​new OttoEngine()'',​ datorită contextului. 
 +</​note>​
  
-            return new BlockInner();​ +<note important> ​ 
-        }+* //Clasele interne statice nu au nevoie de o instanță a clasei externe -> atunci de ce le facem interne acesteia?//​ 
 +  * pentru a grupa clasele, dacă o clasă internă statică A.B este folosită doar de A, atunci nu are rost să o facem top-level. 
 +* //Avem o clasă internă A.B, când o facem statică?//​ 
 +  * în interiorul clasei B nu avem nevoie de nimic specific instanței clasei externe A, deci nu avem nevoie de o instanță a acesteia -> o facem statică 
 +</​note>​
  
-        return null; 
-    } 
-} 
-</​code>​ 
  
-În acest exemplu, clasa internă ''​BlockInner''​ este defintă în cadrul unui bloc //if//, dar acest lucru nu înseamnă că declarația va fi luată în considerare doar la rulare, în cazul în care condiția este adevarată. 
  
-<note important>​Semnificația declarării clasei într-un bloc este legată strict de vizibilitatea acesteia. La compilare clasa va fi creată indiferent care este valoarea de adevăr a condiției //​if//​.</​note> ​+==Clase interne în metode și blocuri==
  
 +Primele exemple prezintă modalitățile cele mai uzuale de folosire a claselor interne. Totuși, design-ul claselor interne este destul de complex și exista modalitati mai "​obscure"​ de a le folosi: clasele interne pot fi definite și în cadrul metodelor sau al unor blocuri arbitrare de cod. 
  
-==Clase anonime== 
  
-Exista multe situații ​în care o clasă internă este instanțiată într-un singur loc (si este folosita prin ''​upcasting''​ la o clasă de bază sau interfață),​ ceea ce face ca numele clasei să nu mai fie important, iar tipul ei poate fi un subtip al unei clase sau o implementare a unei interfețe. Singurele ​metode ​care pot fi apelate pe o clasa anonimă sunt cele are tipului pe care îl extinde sau implementează. ​+=== Clase interne ​în metode ​=== 
  
-In Java putem crea **clase interne anonime** (făra nume) ca în exemplul următor:+În exemplul următor, clasa internă a fost declarată în **interiorul funcției** ''​getInnerInstance''​. În acest mod, vizibilitatea ei a fost redusă pentru ​ca nu poate fi instanțiată decât ​în această funcție. ​
  
-<code java> +Singurii modificatori care pot fi aplicați acestor clase sunt ''​abstract''​ și''​ final'' ​(binențeles,​ nu amândoi deodată).
-interface Hidden { +
-    public int value()+
-}+
  
-class Outer +<code java Test.java>​ 
-    public ​Hidden getInnerInstance(int i) { +[...] 
-        ​return new Hidden() ​+class Car 
-            private int = 11;+    public ​Engine getEngine() { 
 +        ​class OttoEngine implements Engine ​
 +            private int fuelCapacity ​= 11;
  
-            public int value() { +            public int getFuelCapacity() { 
-                return ​i;+                return ​fuelCapacity;
             }             }
-        }+        }
-    } +
-+
- +
-public class Test { +
-    public static void main(String[] args) { +
-        Outer out = new Outer();+
  
-        ​Hidden in3 = out.getInnerInstance(11); +        ​return new OttoEngine();
-        System.out.println(in3.value());+
     }     }
 } }
 +[...]
 </​code>​ </​code>​
  
-Observați modalitatea ​de declarare a clasei anonimeSintaxa ''​return new Hidden() { ... }''​ reprezintă urmatoarele: +<​note>​ 
-* dorim sa întoarcem un obiect ​de tip ''​Hidden''​ +* Schimbați implementarea clasei Car de [[.:clase-interne#​Clase interne "​normale"​|aici]] folosind codul de mai sus 
-acest obiect este instanțiat imediat dupa ''​return''​, folosind ''​new''​ (referința întoarsă de ''​new''​ va fi ''​upcast''​ la clasa de baza: ''​Hidden''​) +Observați că trebuie ​''​OttoEngine'' ​este vizibilă doar în interiorul metodei 
-numele clasei instanțiate este absent (ea este anonimă), însă ea este de ''​tipul'' ​''​Hidden'',​ prin urmare, va implementa metoda/​metodele din interfață(cum e metoda ''​value''​). Corpul clasei urmeaza imediat instanțierii. ​+Modificați metoda ​''​main'' ​astfel încât să ruleze ​(Hint: [[.:​clase-interne#​Clase anonime|upcast]]) 
 +</​note>​
  
-Construcția ''​return new Hidden() { ... }''​ este echivalentă cu a spune: //creează un obiect al unei clase anonime ce implementeaza ''​Hidden''//​. 
  
-<note important>​O clasă internă anonimă poate extinde o clasă //sau// să implementeze o singură interfață, ​nu poate face pe ambele împreună ca la clasele ne-anonime (interne sau nu), și nici nu poate să implementeze mai multe interfețe</​note>​ +<note important>​Clasele interne declarate în metode ​nu pot folosi variabilele declarate în metoda respectivă și nici parametrii metodeiPentru a le putea accesa, variabilele trebuie declarate ''​final'',​ ca în exemplul următor. Această restricție ​se datorează faptului că variabilele si parametrii metodelor se află pe segmentul ​de stivă (zonă de memorie) ​creat pentru metoda respectivă, ceea ce face ca ele să nu existe la fel de mult cât clasa internă. Dacă variabila este declarată ''​final'',​ atunci la runtime se va stoca o copie a acesteia ca un câmp al clasei ​interne, în acest mod putând fi accesată și după execuția metodei. Pentru o explicație detaliată citiți [[ http://​techtracer.com/​2008/​04/​14/​mystery-of-accessibility-in-local-inner-classes/​ | Link1 ]] și [[ http://​stackoverflow.com/​questions/​1945663/​where-are-java-final-local-variables-stored | Link2]]. 
- +</​note>​ 
-=== Constructori === +=== Clase interne ​în blocuri ===  
- ​Clasele anonime **nu** pot avea **constructori** din cauză că nu au nume (nu am ști cum să numim constructorii). Această restricție ​asupra claselor anonime ridică o problemăƒ: în mod implicit, clasă de bază este creată ​cu constructorul //​default//​.  +Exemplu ​de clasa internă declarata într-un ​**bloc**:
- +
-Ce se întâmplă dacムdorim să invocăƒm un **alt constructor** ​al clasei ​de bază? În clasele normale acest lucru era posibil prin apelarea explicită, în prima linie din constructor a constructorului clasei de bazムcu parametrii doriți, folosind ''​super''​În clasele ​interne ​acest lucru se obține prin transmiterea parametrilor căƒtre constructorul clasei ​de bază **direct** la crearea obiectului de tip clasă anonimă+
  
 <code java> <code java>
-new Student("​Andrei"​) { +[...] 
-    ... +class Car { 
-} +    public Engine getEngine(int fuelCapacity) { 
-</​code>​+        if (fuelCapacity == 11) { 
 +            class OttoEngine implements Engine { 
 +                ​private int fuelCapacity = 11;
  
-În acest exemplu, am instanțiat o clasa anonimă, ce extinde clasa ''​Student'',​ apelând constructorul clasei de bază cu parametrul ''"​Andrei"''​.+                public int getFuelCapacity() { 
 +                    return fuelCapacity;​ 
 +                } 
 +            }
  
- +            return new OttoEngine();
- +
-==Clase interne statice== +
- +
-În secțiunile precedente, s-a discutat doar despre clase interne a caror instanțe există doar în contextul unei instanțe a clasei exterioare, astfel că poate accesa membrii obiectului exterior direct. De asemenea, am menționat că fiind membri ai claselor exterioare, clasele interne pot avea modificatorii disponibili pentru metode și variabile, dintre care și ''​static'' ​(clasele exterioare nu pot fi statice!). Așa cum pentru a accesa metodele și variabilele statice ale unei clase nu este nevoie de o instanță a aceteia, putem obține o referință către o clasă internă fără a avea nevoie de o instanță a clasei exterioare.  +
- +
-Pentru a înțelege diferența dintre clasele interne statice și cele nestatice trebuie să reținem următorul aspect: **clasele nestatice țin legătura cu obiectul exterior** în vreme ce **clasele statice nu păstrează această legătură**.  +
-   +
-Pentru clasele interne statice: ​  +
- * nu avem nevoie de un obiect al clasei externe pentru a crea un obiect al clasei interne ​  +
- * nu putem accesa câmpuri nestatice ale clasei externe din clasă internă (nu avem o instanță a clasei externe) +
- +
-<code java Test.java>​ +
-class Outer { +
-    public int outerMember = 9; +
- +
-    class NonStaticInner { +
-        private int i = 1; +
- +
-        public int value() { +
-            return i + Outer.this.outerMember// OK, putem accesa un membru al clasei exterioare+
         }         }
-    } 
  
-    static class StaticInner { +        ​return ​null;
-        public int k = 99; +
- +
-        public int value() { +
-            k += outerMember;​ // EROARE, nu putem accesa un membru nestatic al clasei exterioare +
-            ​return ​k; +
-        } +
-    } +
-+
- +
-public class Test { +
-    public static void main(String[] args) { +
-        Outer out = new Outer(); +
- +
-        Outer.NonStaticInner nonSt = out.new NonStaticInner();​ // instantiere CORECTA pt o clasa nestatica +
-        Outer.StaticInner st = out.new StaticInner(); ​        // instantiere INCORECTA a clasei statice +
-        Outer.StaticInner st2 = new Outer.StaticInner()    // instantiere CORECTA a clasei statice+
     }     }
 } }
 +[...]
 </​code>​ </​code>​
  
-În exemplul ​de mai sus se observă că folosirea membrului nestatic ''​outerMember''​ în clasa statică ''​StaticInner''​ este incorectăDe asemenea, se observă modalitățile diferite de instanțiere a celor două tipuri de clase interne ​(statice și nestatice): ​ +<​note>​ 
- ​* ​folosim o instanță a clasei exterioare - ''​out'' ​(ca și în exemplele anterioare) pentru a instanția o clasă nestatică. ​ +* Schimbați implementarea clasei Car de [[.:clase-interne#​Clase ​interne ​"​normale"​|aici]] folosind codul de mai sus 
- ​* ​folosim numele claselor pentru a instanția o clasă statică. Folosirea lui ''​out'' ​este incorectă. +Observați că trebuie ​''​OttoEngine'' ​este vizibilă doar în interiorul blocului 
 +Modificați metoda ​''​main'' ​astfel încât să ruleze (Hint: [[.:​clase-interne#​Clase anonime|upcast]]) 
 +</​note>​
  
 +În acest exemplu, clasa internă ''​OttoEngine''​ este defintă în cadrul unui bloc //if//, dar acest lucru nu înseamnă că declarația va fi luată în considerare doar la rulare, în cazul în care condiția este adevarată.
  
-<​note> ​ +<​note ​important>Semnificația declarării clasei ​într-un bloc este legată strict ​de vizibilitatea acesteiaLa compilare clasa va fi creată indiferent care este valoarea ​de adevăcondiției //if//​.</​note> ​
- +
-* //Clasele interne statice nu au nevoie de o instanță clasei ​externe ​-> atunci de ce le facem interne acesteia?//​ +
-  * pentru a grupa clasele, dacă o clasă internă statică A.B este folosită doar de A, atunci nu are rost să o facem top-level. +
-* //Avem o clasă internă A.B, când o facem statică?//​ +
-  * în interiorul clasei B nu avem nevoie ​de nimic specific instanței clasei externe A, deci nu avem nevoie de o instanță a acesteia -> o facem statică +
-</​note>​ +
-<note tip>​Terminologia //nested classes// vs //inner classes//:​ +
-Clasele interne normale, cele anonime si cele interne blocurilor si metodelor sunt //inner classes// datorită relației pe care o au cu clasa exterioară (depind de o instanță a acesteia). Termenul de //nested classes// se referă la definirea unei clase în interiorul altei clase, și cuprinde atât //inner classes// cât și clasele statice interne. De aceea, claselor statice interne li se spune //static nested classes// și nu //static inner classes//. +
-</​note>​+
 ==Moștenirea claselor interne== ==Moștenirea claselor interne==
  
-Deoarece constructorul clasei interne trebuie sa se //​atașeze//​ de un obiect al clasei exterioare, moștenirea unei clase interne este puțin mai complicată decât cea obișnuită. Problema rezidă în nevoia de a inițializa legătura (ascunsă) cu clasa exterioară,​ în contextul în care în clasa derivată nu mai există un obiect default pentru acest lucru (care era ''​NumeClasaExterna.this''​).+Deoarece constructorul clasei interne trebuie sa se //​atașeze//​ de un obiect al clasei exterioare, moștenirea unei clase interne este puțin mai complicată decât cea obișnuită. Problema rezidă în nevoia de a inițializa legătura (ascunsă) cu clasa exterioară,​ în contextul în care în clasa derivată nu mai există un obiect default pentru acest lucru.
  
 <code java> <code java>
-class WithInner ​+class Car 
-    class Inner +    class Engine ​
-        public void method() { +        public void getFuelCapacity() { 
-            System.out.println("​I am Inner'​s method");+            System.out.println("​I am a generic Engine");
         }         }
     }     }
 } }
- +  
-class InheritInner ​extends ​WithInner.Inner +class OttoEngine ​extends ​Car.Engine ​
-    ​InheritInner() {+    ​OttoEngine() {
     } // EROARE, avem nevoie de o legatura la obiectul clasei exterioare     } // EROARE, avem nevoie de o legatura la obiectul clasei exterioare
- +  
-    ​InheritInner(WithInner wi) { // OK +    ​OttoEngine(Car car) { // OK 
-        ​wi.super();+        ​car.super();
     }     }
 } }
 + 
 public class Test { public class Test {
     public static void main(String[] args) {     public static void main(String[] args) {
-        ​WithInner wi = new WithInner(); +        ​Car car = new Car(); 
-        ​InheritInner ii = new InheritInner(wi); +        ​OttoEngine ottoEngine ​= new OttoEngine(car); 
-        ​ii.method();+        ​ottoEngine.getFuelCapacity();
     }     }
 } }
 </​code>​ </​code>​
  
-Observăm ca ''​InheritInner''​ moșteneste doar ''​WithInner.Inner''​ însa sunt necesare: +Observăm ca ''​OttoEngine''​ moșteneste doar ''​Car.Engine''​ însa sunt necesare: 
-* parametrul constructorului ''​InheritInner''​ trebuie sa fie de tipul clasei externă (''​WithInner''​) +* parametrul constructorului ''​OttoEngine''​ trebuie sa fie de tipul clasei externă (''​Car''​) 
-* linia din constructorul ''​InheritInner'':​ ''​wi.super()''​.+* linia din constructorul ''​OttoEngine'':​ ''​car.super()''​. 
 + 
 +<​note>​ 
 +* Rulați codul de mai sus 
 +* Ștergeți constructorul ''​OttoEngine()'',​ rulați din nou codul 
 +</​note>​
  
 ==Utilizarea claselor interne== ==Utilizarea claselor interne==
Line 379: Line 303:
  }  }
     });</​code>​     });</​code>​
- == Exerciții ==  +== Exerciții ==  
-   +**Task 1 Meta** (4p)
- ​Implementați un terminal bash simplu pornind de la scheletul de cod. Comenzile pe care va știi să le execute sunt: **echo, cd, ls și history**. Bash-ul va citi comenzi de la tastatură până când va primi comanda ​**exit** când se va închide (programul se termină).  +
-      * În clasa **BashUtils** din pachetul **bash** vom implementa fiecare comandă ca o clasă internă.  +
-      * În clasa **Bash** din pachetul **bash** vom citi comenzi de la tastatură și le vom trimite spre **BashUtils** spre a fi executate.  +
-      * Mecanismul de funcționare va fi de tipul [[ https://​en.wikipedia.org/​wiki/​Publish%E2%80%93subscribe_pattern | Publisher-Subscriber ]].  +
-          * Concret: Bash-ul va citi comenzile de la tastatură și le va **publica** către toți subscriberii săi. Bash-ul va funcționa ca un **Publisher**.  +
-          * Utilitarele,​ **ls, echo, cd și history**, care știu cum să execute comenzile se vor înregistra la **Publisher** folosind metoda **subscribe** a acestuia și vor fi anunțate când o nouă comandă este primită. Ele vor funcționa ca **Subscriberi**.  +
-   +
- ​{{ ​ :​laboratoare:​clase-interne:​publish_subscribe.gif ​ ?  | Publisher-Subscriber ​ }}  +
- ​{{ ​ :​laboratoare:​clase-interne:​publish_subscribe_details_v3.png ​ ?  | BashCommands Publisher-Subscriber ​ }}  +
-   +
-      * În scheletul de cod veți găsi 2 interfețe:  +
-          * **CommandPublisher** având metodele:  +
- <​code Java>  +
-    /** +
-     * Subscribe a CommandSubscriber to this Publisher so that it is +
-     * notified when a command is received. +
-     */ +
-    void subscribe(CommandSubscriber subscriber);+
  
-    /** +Îndrumarul este o componentă importantă a laboratorului ​de POO, iar citirea ​și înțelegerea acestuia nu trebuie neglijate. În îndrumarul din acest laborator există mai multe căsuțde tip **note**de forma celei de mai jos
-     * Publish a Command to all registered subscribers. + 
-     * Usually by calling the method defined in the CommandSubscriber interface. +<​note> ​Do stuff </​note>​
-     */ +
-    void publish(Command command); +
- </​code>​  +
-          * **CommandSubscriber** având metoda:  +
- <​code Java>  +
-    /** +
-     * Executes the given command. +
-     * We can call this method from a Publisher to tell the Subscriber that a command was received. +
-     */ +
-    void executeCommand(Command c); +
- </​code>​  +
-     * Observăm că un **CommandPublisher** face **publish** la un obiect ​de tip **Command**, iar un **CommandSubscriber** primește în metoda **executeCommand** un astfel de obiect ca parametru.  +
-         * Găsiți în scheletul de cod implementarea clasei **Command**. Acesta este obiectul prin care Publisherul și Subscriberii comunică aka își trimit date.   +
-   +
- ​**[TODO 1] (1.5p)** Din clasa **Bash** vom **publica** comenzile prin interfața **CommandPublisher**. În acest sens în clasa **Bash** vom crea o clasă internă **BashCommandPublisher** ce implementează interfața **CommandPublisher**. ​  +
-        * În această clasă creați o [[ https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​util/​ArrayList.html | listă ]] de elemente ​de tip CommandSubscriber. ​  +
-        ​Apoi va trebui să implementați metodele:  +
-            ​* **subscribe**-prin care adăugăm un subscriber în lista de subscrieri  +
-            * **publish**-în care iteram prin lista de subscriberi și trimitem evenimentul către subscriberi apelând metoda definită în interfața **CommandSubscriber** (în cazul de față **executeCommand**)  +
-   +
- ​**[TODO 2] (1p)** Un obiect de tipul **Bash** va avea:   +
-          * un director current reținut în membrul **currentDirectory** care este de tipul [[ https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​nio/​file/​Path.html | Path ]]   +
-          * un istoric al comenzilor care este de tipul [[ https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​StringBuffer.html | StringBuffer ]] .   +
-   +
- <​note>​ De ce este mai util să folosim un StringBuffer și nu un String simplu?  +
- [[ http://​www.javaworld.com/​article/​2076072/​build-ci-sdlc/​stringbuffer-versus-string.html | Sting vs StringBuffer ]]?  ​ +
- </​note>​  +
-         * În constructorul clasei **Bash** vom inițializa **history** și apoi **currentDirectory** cu calea către directorul curent "​."​. Hint: [[ https://​docs.oracle.com/​javase/​tutorial/​essential/​io/​pathOps.html | Paths.get ]]  +
-         * Instantiati și obiectul de tip **CommandPublisher** definit la exercițiul anterior. Prin intermediul lui vom publica comenzi din **Bash** în sistem.  +
-   +
- ​**[TODO 3] (2p)** În metoda **start** din Bash vom citi de la tastatură comenzi pe câte o linie folosind [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​util/​Scanner.html | Scanner]].  +
-     * Când se citește string-ul **exit** programul se termină.  +
-     * Pentru orice altă comandă vom crea un nou [[https://​docs.oracle.com/​javase/​tutorial/​essential/​concurrency/​runthread.html | Java Thread]] de pe care vom **publica** comanda către subscriberi prin instanța de **CommandPublisher** creată la exercițiul 1.  +
-        * Creați și instantiati o clasă internă anonimă ce **extinde** clasa **Thread**. Clasa va trebui să implementeze metoda **run** prin care îi spunem thread-ului ce să facă (în cazul nostru să apeleze metoda **publish**).  +
-  ​ +
- <​note ​important +
- ​Pentru a porni un Thread apelăm metoda **start** a acestuia. Implementarea metodei **run** și instantierea Thread-ului nu îl lansează în execuție.  +
- </​note> ​ +
-   +
- <​code Java>  +
- ​Thread t = new Thread() {  +
-     ​public void run() {  +
-         // Do some work  +
-     }  +
- };  +
- ​t.start();​  +
- </​code> ​   +
-       +
- ​**[TODO 4] (1p)** Implementați comanda **echo** ca o clasă internă în **BashUtils**.  +
-     * Clasa va trebui să implementeze interfața **CommandSubscriber**.  +
-     * Metoda **executeCommand** va trebui să:  +
-         * verifice dacă comanda primită începe cu "​echo"​. Altfel nu va trebui să facă nimic.  +
-         * să afișeze la consolă șirul aflat după cuvântul cheie "​echo"​  +
- <​code>​  +
->echo bla  +
- bla  +
- </​code>​  +
-   +
-     * Creați un obiect de tipul clasei **Echo** în constructorul clasei **Bash**. Înregistrați obiectul ca **subscriber** la instanța de **CommandPublisher** folosind metoda **subscribe**.  +
-     * Testați rulând metoda **main** din clasa **LinuxOS**. ​+
  
- ​**[TODO 5] (1.5p)** Implementați comanda **cd** care schimbă directorul curent.  +Identificați toate căsuțele de tip **note** din laborator și urmați instrucțiunile din acesteaPuteți folosi scheletul pus la dispoziție. Pentru ​ușura procesul ​de evaluare, creați fișiere separate pentru fiecare task din **note**. 
-     * Clasa va trebui să implementeze interfaț**CommandSubscriber**.  + 
-     * Metoda **executeCommand** va trebui să:  + 
-         * verifice dacă comanda primită începe cu "​cd"​.  +**Task 2 - Car Dealership: The Beginning** ​(2p) 
-         * să schimbe variabila **currentDirectory** cu noua cale. Funcția **cd** va face **append** la calea deja existentă în **currentDirectory**. Hint: [[ https://​docs.oracle.com/​javase/​tutorial/​essential/​io/​pathOps.html | Paths.get ]]  + 
- <​code>​  +Scheletul de cod conține implementarea unui dealership de mașini. Acesta vinde autoturisme doar la cerere, conform cu tipul de mașină cerut de client. Totuși, patronul este nemulțumit. El ar dori ca: 
- ​currentDirectory = E:​FacultateLab05.  +toate tipurile de mașini vândute ​să nu fie vizibile decât de către angajați 
-   +dealership-ul ​să rămână unul cu specific ​și tradiție, continuând ​să vândă veșnic tipurile curente de mașini  
->cd .idea  + 
-   +''​Hint:​ aceasta este o decizie de business teribilă din partea patronuluidar acesta v-a promis că veți putea închiria orice mașină de la el, dar doar după ce îl ajutați.''​ 
- ​currentDirectory = E:​FacultateLab05..idea  + 
-   +** Task 3 - Car Dealership: The Crisis ​** (2p) 
- </​code>​  + 
-     * Creați un obiect de tipul clasei **Cd** în constructorul clasei **Bash**Înregistrați obiectul ca **subscriber** ​la instanța de **CommandPublisher** folosind metoda **subscribe**.  +Timpul a trecut peste dealership, iar vânzările au fost chiar bune, cu o singură excepție, mașinile ​de tip ''​RACING''​Aceste tipuri de mașini sunt făcute pe comandă, iar cererea a fost atât de mică, încât nici după un an nu s-au vândut cele din primul lot. Astfel, dealership-ul riscă să dea faliment, din cauza datoriilor generate ​(mașinile de tip ''​RACING''​ sunt și cele mai scumpe). 
-     * Testați rulând metoda **main** ​din clasa **LinuxOS**.  + 
-   +Din fericire, patronul a făcut o înțelegere cu ''​Ferrari'',​ de unde a șcumpărat mașinile. Aceștia îl vor scăpa complet ​de datorii dacă acesta va elimina orice asociere dintre ''​Dealership''​ și ''​Ferrari'',​ lăsând totuși posibilitatea pentru clienți de a-și ridica mașinile ​de la ''​Dealership''​Patronul este disperat șvă cere din nou ajutorulVa trebui să eliminați orice asociere dintre cele două entități, păstrând funcționalitatea aplicației
- **[TODO 6] (2p)** Implementați comanda **ls** care afișează conținutul directorului curent **currentDirectory** ​ + 
-     ​Clasa va trebui ​să implementeze interfața **CommandSubscriber**. ​ +''​HintClienții vor mai putea să își ridice mașinile Ferrari, dar acestea nu vor mai fi administrate de către Dealership.''​ 
-     ​Metoda **executeCommand** va trebui ​să:  + 
-         * verifice dacă comanda primită este "​ls"​ fără parametrii.  +** Task 4 - Car Dealership: The New Age ** (2p) 
-         * să itereze prin conținutul directorului curent ​și să afișeze numele fișierelor la consolă, pe câte o linie fiecare. Hint: [[ http://​stackoverflow.com/​questions/​1844688/​read-all-files-in-a-folder | cum citim conținutul unui director în Java? ]]  + 
- <​code>​  +Business-ul merge bine, astfel că patronul nostru ​s-a hotărât să se extindă. El vrea să aducă în ''​Dealership''​ mașini de tip ''​ELECTRIC'',​ ''​Tesla'',​ și a vorbit chiar cu **Elon Musk** în acest sensAceastă extindere ​este una importantăastfel că patronul nostru vrea o performanță cât mai ridicată. Patronul vă roagă să îl ajutați cu o comparație între: 
->ls  + 
- ​E:​FacultateLab05.  +comandarea mașinilor ''​Tesla''​ folosind clase anonime 
- .idea  +comandarea mașinilor ''​Tesla''​ folosind funcții lambda 
- clase-interne2.iml  + 
- out  +Un tutorial despre folosirea funcțiilor lambda îl găsiți în laborator, la secțiunea [[.:​clase-interne#​Clase anonime|Clase anonime]]. (opțional) Pentru o mică prezentare în discuția ''​Anonymous vs. Lambda'',​ puteți verifica secțiunea de ''​Referințe''​ a laboratorului.
- src  +
- </​code>​  +
-         * **currentDirectory** are tipul **Path**. Putem obține un obiect ​de tip **File** astfel:  +
- <​code java>  +
- File folder = currentDirectory.toFile() +
- </​code>  ​ +
-   +
-     * Creați un obiect ​de tipul clasei **Ls** în constructorul clasei **Bash**. Înregistrați obiectul ca **subscriber** la instanța de **CommandPublisher** folosind metoda **subscribe** +
-     * Testațrulând metoda **main** ​din clasa **LinuxOS** +
-   +
- ** [TODO 7] (1p)** Implementați comanda **history** care afișează la consolă conținutul membrului **StringBuffer history** din **Bash**.  +
- <​code> ​ +
->history  +
- ​History isls |  | cd .idea | ls | history |  ​ +
- </​code> ​ +
-   +
-     Clasa va trebui să implementeze interfața ​**CommandSubscriber**.  +
-     * Metoda **executeCommand** va trebui ​:  +
-         ​* ​să adauge fiecare comanda primită în StringBuffer-ul ​**history** Hint: [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​StringBuffer.html | StringBuffer append]]  +
-   +
-         * dacă comandă primită este "​history"​să afișeze la consolă conținutul lui **history**  +
-   +
-     * Creați un obiect de tipul clasei **History** ​în constructorul clasei **Bash**Înregistrați obiectul ca **subscriber** la instanțde **CommandPublisher** folosind metoda **subscribe**.  +
-     * Testați rulând metoda **main** din clasa **LinuxOS**+
 == Resurse == == Resurse ==
  
-* {{.:​clase-interne:​clase-interne-skel.zip| Schelet}} +* {{:​laboratoare:​clase-interne:​lab06-clase-interne-schelet.zip| Schelet}} 
-<​html><​a class="​media mediafile mf_pdf"​ href="/​poo/​laboratoare/​clase-interne?​do=export_pdf">​PDF laborator</​a></​html>​ +[[.:old-exercises#clase-interne| ​Old exercises]] 
-* **{{:laboratoare:​clase-interne: ​clase-interne-sol.zip ​Soluție}}**+
 == Referințe == == Referințe ==
   - Kathy Sierra, Bert Bates. //SCJP Sun Certified Programmer for Java™ 6 - Study Guide//. Chapter 8 - Inner Classes ([[http://​firozstar.tripod.com/​_darksiderg.pdf|available online]])   - Kathy Sierra, Bert Bates. //SCJP Sun Certified Programmer for Java™ 6 - Study Guide//. Chapter 8 - Inner Classes ([[http://​firozstar.tripod.com/​_darksiderg.pdf|available online]])
 +  - [[https://​www.oracle.com/​technetwork/​java/​jvmls2013kuksen-2014088.pdf]]
laboratoare/clase-interne.1534508081.txt.gz · Last modified: 2018/08/17 15:14 by Adriana Draghici