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:24]
Laurentiu Stamate [Moștenirea claselor 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 anonime (//​anonymous inner classes//) * clase anonime (//​anonymous inner classes//)
Line 26: Line 22:
 * 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 acesteia, altfel 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''​. 
  
  
Line 125: Line 102:
  
 ==Clase anonime== ==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 Explosion, care, pe scurt, ar aduce după sine o aplicație cu performanțe scăzute și greu de modificat/​extins.
  
-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ă. ​+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.
  
-In Java putem crea **clase interne anonime** ​(făra numeca în exemplul următor:+Întorcându-ne la exemplul cu clasa top-level ''​Car'', ​putem rescrie metoda ''​getEngine()''​ a acesteia astfel:
  
 <code java> <code java>
-interface Hidden { +[...] 
-    public int value(); +class Car 
-+    public ​Engine getEngine(int fuelCapacity) { 
- +        return new Engine ​() { 
-class Outer +            private int fuelCapacity ​= 11;
-    public ​Hidden getInnerInstance(int i) { +
-        return new Hidden() { +
-            private int = 11;+
  
-            public int value() { +            public int getFuelCapacity() { 
-                return ​i;+                return ​fuelCapacity;
             }             }
         };         };
     }     }
 } }
 +[...]
 +</​code>​
  
-public class Test { +<​note>​ Modificați implementarea clasei CarRulați codulUrmați instrucțiunile de mai jos pentru a restabilit funcționalitatea programuluiAdresați-vă asistentului pentru neclarități.</note>
-    public static void main(String[] args) { +
-        Outer out = new Outer(); +
- +
-        Hidden in3 = out.getInnerInstance(11);​ +
-        System.out.println(in3.value()); +
-    } +
-+
-</code>+
  
-Observațmodalitatea de declarare a clasei anonime. Sintaxa ''​return new Hidden() { ... }'' ​reprezintă urmatoarele: +Metoda folosită mai sus elimină necesitatea creări unei clase interne "​normale",​ reducând volumul codului ș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"​.
-* dorim sa întoarcem un obiect de tip ''​Hidden''​ +
-* acest obiect este instanțiat imediat dupa ''​return'',​ folosind ''​new''​ (referința întoarsă de ''​new''​ va fi ''​upcast''​ la clasa de baza: ''​Hidden''​) +
-* 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+
  
-Construcția ''​return new Hidden() { ... }'' ​este echivalentă cu a spune//creează un obiect al unei clase anonime ce implementeaza ​''​Hidden''/​/.+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.
  
 +<note tip>​IntelliJ sugerează înlocuirea cu funcții lambda însă acest concept nu este acoperit în laborator. Pentru detalii suplimentare urmăriți acest [[https://​www.oracle.com/​webfolder/​technetwork/​tutorials/​obe/​java/​Lambda-QuickStart/​index.html#​section2|exemplu]]</​note>​
 + 
 <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>​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>​
  
 === Constructori === === Constructori ===
- ​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//​. ​+ ​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//​. ​
  
-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ă: ​+Ce se întâmplă dacă dorim să invocă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") { +new Engine("Otto") { 
-    ...+    ​// ...
 } }
 </​code>​ </​code>​
  
-În acest exemplu, am instanțiat o clasa anonimă, ce extinde clasa ''​Student'',​ apelând constructorul clasei de bază cu parametrul ''"​Andrei"''​. +În acest exemplu, am instanțiat o clasa anonimă, ce implementează interfața ​''​Engine'',​ apelând constructorul clasei de bază cu parametrul ''"​Otto"''​.
- +
  
  
 ==Clase interne statice== ==Clase interne statice==
  
-În secțiunile precedente, s-a discutat doar despre clase interne ​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. ​+Î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. ​
  
 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 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ă**. ​
Line 192: Line 160:
  * nu putem accesa câmpuri nestatice ale clasei externe din clasă internă (nu avem o instanță a clasei externe)  * 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 { +<​note>​ 
-        ​private int i = 1; +* 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()''​. 
-        public int value() { +Rulați codul. Observați că în interiorul ​clasei ''​Car''​, putem instanția ​clasa internă statică ​folosind doar ''​new OttoEngine()''​, datorită contextului
-            return i + Outer.this.outerMember;​ // OKputem accesa un membru al clasei exterioare +</note>
-        } +
-    } +
- +
-    static class StaticInner { +
-        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>​ +
- +
-Î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) +
- ​* ​folosim o instanță clasei ​exterioare - ''​out'' ​(ca și în exemplele anterioare) pentru a instanția ​o clasă nestatică.  +
- * folosim numele claselor pentru a instanția o clasă statică. Folosirea lui ''​out'' ​este incorectă.  +
- +
- +
-<​note> ​+
  
 +<note important> ​
 * //Clasele interne statice nu au nevoie de o instanță a clasei externe -> atunci de ce le facem interne acesteia?// * //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.   * 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ă?// * //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ă   * î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>​ </​note>​
- 
  
  
Line 256: Line 188:
  
 <code java Test.java>​ <code java Test.java>​
-interface Hidden { +[...] 
-    public int value(); +class Car 
-+    public ​Engine getEngine() { 
- +        class OttoEngine ​implements ​Engine ​
-class Outer +            private int fuelCapacity ​= 11;
-    public ​Hidden getInnerInstance() { +
-        class FuncInner ​implements ​Hidden ​+
-            private int = 11;+
  
-            public int value() { +            public int getFuelCapacity() { 
-                return ​i;+                return ​fuelCapacity;
             }             }
         }         }
  
-        return new FuncInner(); +        return new OttoEngine();
-    } +
-+
- +
-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>​
  
- +<​note>​ 
-<​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.+* Schimbați implementarea clasei Car de [[.:clase-interne#Clase interne "​normale"​|aici]] folosind codul de mai sus 
 +* Observați că trebuie ''​OttoEngine'' ​este vizibilă doar în interiorul metodei 
 +* Modificațmetoda ''​main'' ​astfel încât să ruleze (Hint: [[.:​clase-interne#​Clase anonime|upcast]])
 </​note>​ </​note>​
-<code java> 
-public void f() { 
-    final Student s = new Student(); 
-    // s trebuie declarat final ca sa poata fi accesat din AlterStudent 
  
-    class AlterStudent { 
-        public void alterStudent() { 
-            s.name = ...               // OK 
-            s = new Student(); ​        // GRESIT! 
-        } 
-    } 
-} 
-</​code>​ 
  
 +<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. 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>​
 === Clase interne în blocuri ===  === Clase interne în blocuri === 
 Exemplu de clasa internă declarata într-un **bloc**: Exemplu de clasa internă declarata într-un **bloc**:
  
 <code java> <code java>
-interface Hidden { +[...] 
-    public int value(); +class Car 
-+    public ​Engine getEngine(int fuelCapacity) { 
- +        if (fuelCapacity ​== 11) { 
-class Outer +            class OttoEngine ​implements ​Engine ​
-    public ​Hidden getInnerInstance(int i) { +                private int fuelCapacity ​= 11;
-        if (== 11) { +
-            class BlockInner ​implements ​Hidden ​+
-                private int = 11;+
  
-                public int value() { +                public int getFuelCapacity() { 
-                    return ​i;+                    return ​fuelCapacity;
                 }                 }
             }             }
  
-            return new BlockInner();+            return new OttoEngine();
         }         }
  
Line 330: Line 236:
     }     }
 } }
 +[...]
 </​code>​ </​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>​ 
 +* Schimbați implementarea clasei Car de [[.:​clase-interne#​Clase interne "​normale"​|aici]] folosind codul de mai sus 
 +* 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 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> ​ <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> ​
- 
- 
- 
- 
- 
- 
 ==Moștenirea claselor interne== ==Moștenirea claselor interne==
  
Line 346: Line 253:
  
 <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 391: 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.1534508675.txt.gz · Last modified: 2018/08/17 15:24 by Laurentiu Stamate