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/10/30 15:22]
Veronica Radu [Modificatorii de acces pentru 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 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>​
 +interface Engine {
 +    public int getFuelCapacity();​
 +}
 +
 class Car { class Car {
-    class Engine {+    class OttoEngine implements ​Engine {
         private int fuelCapacity;​         private int fuelCapacity;​
  
-        public ​Engine(int fuelCapacity) {+        public ​OttoEngine(int fuelCapacity) {
             this.fuelCapacity = fuelCapacity;​             this.fuelCapacity = fuelCapacity;​
         }         }
Line 45: Line 51:
     }     }
  
-    public ​Engine ​getEngine() { +    public ​OttoEngine ​getEngine() { 
-        ​Engine ​engine = new Engine(11);+        ​OttoEngine ​engine = new OttoEngine(11);
         return engine;         return engine;
     }     }
Line 55: Line 61:
         Car car = new Car();         Car car = new Car();
  
-        Car.Engine ​firstEngine = car.getEngine();​ +        Car.OttoEngine ​firstEngine = car.getEngine();​ 
-        Car.Engine ​secondEngine = car.new ​Engine(10);+        Car.OttoEngine ​secondEngine = car.new ​OttoEngine(10);
  
-        System.out.println(firstEngine.value()); +        System.out.println(firstEngine.getFuelCapacity()); 
-        System.out.println(secondEngine.value());+        System.out.println(secondEngine.getFuelCapacity());
     }     }
 } }
 </​code>​ </​code>​
  
-În exemplul de mai sus, o dată ce avem o instanță a clasei ​Car, sunt folosite două modalități de a obține o instanță a clasei ''​Engine''​ (definită în interiorul clasei ''​Car''​):​ +<code html> 
-* definim o metodă ''​getEngine'',​ care creează și întoarce o astfel de instanță;​ +student@poo:​~$ javac Test.java 
-* instanțiem efectiv ''​Engine'';​ observați cu atentie sintaxa folosita! Pentru a instanția ''​Engine'',​ avem nevoie de o instanta ''​Car'':​ ''​car.new Engine(10);''​+student@poo:​~$ ls 
 +Car.class ​Car$OttoEngine.class Engine.class Test.class Test.java 
 +</​code>​ 
 + 
 +Urmăriți exemplul ​de folosire ​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 ''​Car''​) 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//:
Line 74: Line 84:
 </​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 Engine { +
-    public int getFuelCapacity();​ +
-}+
  
-class Car +* Tipul ''​Car.OttoEngine''​ nu mai poate fi accesat din exterior. Acest neajuns poate fi rezolvat cu ajutorul interfeței ''​Engine''​. Asociindu-i clasei interne ''​Car.OttoEngine''​ supertipul ''​Engine''​ prin moștenire, putem instanția clasa prin **upcasting**.
-    private class FerrariEngine implements ​Engine ​+
-        private int fuelCapacity;​+
  
-        public FerrariEngine(int fuelCapacity) { +* 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.fuelCapacity = fuelCapacity;​ +
-        }+
  
-        public int getFuelCapacity() { 
-            return fuelCapacity;​ 
-        } 
-    } 
- 
-    public Engine getEngine() { 
-        FerrariEngine engine = new FerrariEngine(11);​ 
-        return engine; 
-    } 
-} 
- 
-public class Car { 
-    public static void main(String[] args) { 
-        Car car = new Car(); 
- 
-        Car.FerrariEngine firstEngine = car.getEngine(); ​ // va genera eroare, deoarece 
-                                                        // tipul Car.FerrariEngine nu este vizibil 
-        Car.FerrariEngine secondEngine = new Car().new FerrariEngine(10);​ // din nou eroare 
- 
-        Engine thirdEngine = car.getEngine(); ​          // acces corect la o instanta FerrariEngine 
-        System.out.println(thirdEngine.getFuelCapacity());​ 
-    } 
-} 
-</​code>​ 
- 
-Observați definirea interfeței ''​Engine''​. Ea este utilă pentru a putea **asocia** clasei ''​FerrariEngine''​ 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 ''​FerrariEngine''​. Cum clasa internă a fost declarată ''​private'',​ acest tip nu mai este vizibil in exteriorul clasei ''​Car''​. 
  
  
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 (și este folosită 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 ale 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.
  
-În Java putem crea **clase interne anonime** ​(fără 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 Engine { +[...]
-    public int getFuelCapacity();​ +
-+
 class Car { class Car {
     public Engine getEngine(int fuelCapacity) {     public Engine getEngine(int fuelCapacity) {
Line 146: Line 121:
     }     }
 } }
- +[...]
-public class Test { +
-    public static void main(String[] args) { +
-        Car car = new Car(); +
- +
-        Engine engine = car.getEngine(11);​ +
-        System.out.println(engine.getFuelCapacity());​ +
-    } +
-}+
 </​code>​ </​code>​
  
-<​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>​+<​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>​
  
-Observațmodalitatea de declarare a clasei anonime. Sintaxa ''​return new Engine() { ... }'' ​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 ''​Engine''​ +
-* acest obiect este instanțiat imediat dupa ''​return'',​ folosind ''​new''​ (referința întoarsă de ''​new''​ va fi ''​upcast''​ la clasa de baza: ''​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 ''​value''​). Corpul clasei urmeaza imediat instanțierii+
  
-Construcția ''​return new Engine() { ... }'' ​este echivalentă cu a spune//creează un obiect al unei clase anonime ce implementeaza ​''​Engine''/​/.+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>​
  
Line 174: Line 142:
  
 <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"''​.
- +
  
  
Line 194: 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 Student { 
-    public int grade = 9; 
  
-    class Gradebook { +<​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 updateGrade() { +Rulați codul. Observați că în interiorul ​clasei ''​Car''​, putem instanția ​clasa internă statică ​folosind doar ''​new OttoEngine()''​, datorită contextului
-            return i + Student.this.grade; // OKputem accesa un membru al clasei exterioare +</note>
-        } +
-    } +
- +
-    static class EvilGradebook { +
-        public int k = 99; +
- +
-        public int updateGrade() { +
-            k += grade; // EROARE, nu putem accesa un membru nestatic al clasei exterioare +
-            return k; +
-        } +
-    } +
-+
- +
-public class Test { +
-    public static void main(String[] args) { +
-        Student student = new Student();​ +
- +
-        Student.Gradebook out = student.new Gradebook();​ // instanțiere CORECTĂ pentru o clasă nestatică +
-        Student.EvilGradebook badStudent = student.new EvilGradebook(); ​        // instanțiere INCORECTĂ a clasei statice +
-        Student.EvilGradebook smartStudent = new Student.EvilGradebook(); ​    // instanțiere CORECTĂ a clasei statice +
-    } +
-+
-</​code>​ +
- +
-În exemplul de mai sus se observă că folosirea membrului nestatic ​''​grade'' ​în clasa statică ''​EvilGradebook''​ 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 - ''​student'' ​(ca și în exemplele anterioare) pentru a instanția ​o clasă nestatică.  +
- * folosim numele claselor pentru a instanția o clasă statică. Folosirea lui ''​student'' ​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 258: Line 188:
  
 <code java Test.java>​ <code java Test.java>​
-interface Engine { +[...]
-    public int getFuelCapacity();​ +
-+
 class Car { class Car {
     public Engine getEngine() {     public Engine getEngine() {
-        class FerrariEngine ​implements Engine {+        class OttoEngine ​implements Engine {
             private int fuelCapacity = 11;             private int fuelCapacity = 11;
  
Line 272: Line 199:
         }         }
  
-        return new FerrariEngine(); +        return new OttoEngine();
-    } +
-+
- +
-public class Test { +
-    public static void main(String[] args) { +
-        Car car = new Car(); +
- +
-        Car.FerrariEngine badEngine = car.getEngine();​ +
-        // EROARE: clasa FerrariEngine nu este vizibila +
-         +
-        Engine goodEngine = car.getEngine();​ +
- +
-        System.out.println(goodEngine.getFuelCapacity());+
     }     }
 } }
 +[...]
 </​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 = "​Andrei" ​         // 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 Engine { +[...]
-    public int getFuelCapacity();​ +
-+
 class Car { class Car {
     public Engine getEngine(int fuelCapacity) {     public Engine getEngine(int fuelCapacity) {
         if (fuelCapacity == 11) {         if (fuelCapacity == 11) {
-            class FerrariEngine ​implements Engine {+            class OttoEngine ​implements Engine {
                 private int fuelCapacity = 11;                 private int fuelCapacity = 11;
  
Line 326: Line 230:
             }             }
  
-            return new FerrariEngine();+            return new OttoEngine();
         }         }
  
Line 332: Line 236:
     }     }
 } }
 +[...]
 </​code>​ </​code>​
  
-În acest exemplu, clasa internă ''​FerrariEngine''​ 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 356: Line 261:
 } }
    
-class FerrariEngine ​extends Car.Engine { +class OttoEngine ​extends Car.Engine { 
-    ​FerrariEngine() {+    ​OttoEngine() {
     } // EROARE, avem nevoie de o legatura la obiectul clasei exterioare     } // EROARE, avem nevoie de o legatura la obiectul clasei exterioare
    
-    ​FerrariEngine(Car car) { // OK+    ​OttoEngine(Car car) { // OK
         car.super();​         car.super();​
     }     }
Line 368: Line 273:
     public static void main(String[] args) {     public static void main(String[] args) {
         Car car = new Car();         Car car = new Car();
-        ​FerrariEngine ferrariEngine ​= new FerrariEngine(wi); +        ​OttoEngine ottoEngine ​= new OttoEngine(car); 
-        ​ferrariEngine.getFuelCapacity();​+        ​ottoEngine.getFuelCapacity();​
     }     }
 } }
 </​code>​ </​code>​
  
-Observăm ca ''​FerrariEngine''​ moșteneste doar ''​Car.Engine''​ însa sunt necesare: +Observăm ca ''​OttoEngine''​ moșteneste doar ''​Car.Engine''​ însa sunt necesare: 
-* parametrul constructorului ''​FerrariEngine''​ trebuie sa fie de tipul clasei externă (''​Car''​) +* parametrul constructorului ''​OttoEngine''​ trebuie sa fie de tipul clasei externă (''​Car''​) 
-* linia din constructorul ''​FerrariEngine'':​ ''​car.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 393: 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 ]]. +
  
- ​{{  ​:laboratoare:​clase-interne:​publish_subscribe.gif ​ ?  | Publisher-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țe de tip **note**, de forma celei de mai jos:
  
-          * 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**.  +<​note> ​Do stuff </​note>​
-          * 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_details_v3.png ​ ?  | BashCommands Publisher-Subscriber ​ }}  +
-   +
-      * În scheletul de cod veți găsi 2 interfețe:  +
-          * **CommandPublisher** având metodele:  +
-              * subscribe(CommandSubscriber) +
-              * publish(Command) +
-          * **CommandSubscriber** având metoda:  +
-              * executeCommand(Command +
-     * 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.   +
-   +
-    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**)  +
-   +
-    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.  +
-   +
-    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();​  +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**.
- </​code> ​   +
-       +
-    4. **(1p)** Implementați comanda **echo** ca o clasă internă în **BashUtils**.  +
-     * Clasa va trebui să implementeze interfaț**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" ​   +
-     * 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**. +
  
-5. **(1.5p)** Implementați comanda **cd** care schimbă directorul curent. ​ 
-     * Clasa va trebui să implementeze interfața **CommandSubscriber**. ​ 
-     * Metoda **executeCommand** va trebui să:  
-         * verifice dacă comanda primită începe cu "​cd"​. ​ 
-         * 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 ]]  
-     * 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**. ​ 
-     * Testați rulând metoda **main** din clasa **LinuxOS**. ​ 
  
-6. **(2p)** Implementați comanda **ls** care afișează conținutul directorului curent **currentDirectory**  +**Task 2 - Car DealershipThe Beginning** (2p)
-     * Clasa va trebui să implementeze interfața **CommandSubscriber**.  +
-     * Metoda **executeCommand** va trebui să +
-         verifice dacă comanda primită este "​ls"​ fără parametrii.  +
-         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? ]]  +
-         * **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ți rulând metoda **main** din clasa **LinuxOS**. ​  +
  
-7. **(1p)** Implementați comanda **history** care afișează la consolă conținutul membrului **StringBuffer history** din **Bash** +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țumitEl ar dori ca
- <​code>​ +toate tipurile de mașini vândute ​să nu fie vizibile decât de către angajați 
->history  +dealership-ul ​să rămână unul cu specific și tradiție, continuând ​să vândă veșnic tipurile curente de mașini  
- ​History isls |  | cd .idea | ls | history |</​code> ​   + 
-     ​Clasa va trebui ​să implementeze interfața **CommandSubscriber**. ​ +''​Hint:​ aceasta este o decizie de business teribilă din partea patronului, dar acesta v-a promis că veți putea închiria orice mașină de la el, dar doar după ce îl ajutați.''​ 
-     ​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]] ​ +** Task 3 - Car Dealership: The Crisis ​** (2p) 
-         * 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ț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 lotAstfel, 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 și 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 și vă cere din nou ajutorul. Va trebui să eliminați orice asociere dintre cele două entități, păstrând funcționalitatea aplicației. 
 + 
 +''​Hint:​ Clienții vor mai putea să îșridice mașinile Ferrari, dar acestea nu vor mai fi administrate ​de către Dealership.''​ 
 + 
 +** Task 4 - Car Dealership: The New Age ** (2p) 
 + 
 +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: 
 + 
 +comandarea mașinilor ''​Tesla'' ​folosind ​clase anonime 
 +comandarea mașinilor ''​Tesla''​ folosind funcții lambda 
 + 
 +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.
 == 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]] 
 == 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.1540905762.txt.gz · Last modified: 2018/10/30 15:22 by Veronica Radu