This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
laboratoare:agregare-mostenire [2018/08/11 17:58] Adriana Draghici [Agregare și moștenire] |
laboratoare:agregare-mostenire [2019/10/13 22:03] Adriana Draghici |
||
---|---|---|---|
Line 27: | Line 27: | ||
private Bar bar; | private Bar bar; | ||
| | ||
- | // The Bar object can continue to exist even if the Foo object doesn't exist | + | // Obiectul Bar poate continua să existe chiar dacă obiectul Foo nu există |
Foo(Bar bar) { | Foo(Bar bar) { | ||
this.bar = bar; | this.bar = bar; | ||
Line 47: | Line 47: | ||
class Book { | class Book { | ||
- | private String title; // Composition | + | private String title; // Compunere |
- | private Page[] pages; // Composition | + | private Page[] pages; // Compunere |
- | private LibraryRow libraryRow = null; // Aggregation | + | private LibraryRow libraryRow = null; // Agregare |
public Book(int size, String title, LibraryRow libraryRow) { | public Book(int size, String title, LibraryRow libraryRow) { | ||
Line 64: | Line 64: | ||
class LibraryRow { | class LibraryRow { | ||
- | private String rowName = null; // Aggregation | + | private String rowName = null; // Agregare |
| | ||
public LibraryRow(String rowName) { | public LibraryRow(String rowName) { | ||
Line 77: | Line 77: | ||
Book book = new Book(100, "title", row); | Book book = new Book(100, "title", row); | ||
| | ||
- | // After there is no reference to the Book object, the | + | // După ce nu mai există nici o referință la obiectul Carte, |
- | // Garbage Collector will delete (at a certain time, not | + | // Garbage Collector-ul va șterge (la un moment dat, nu |
- | // necessarily immediately) that instance, but the LibraryRow | + | // neapărat imediat) acea instanță, dar obiectul LibraryRow |
- | // object passed to the constructor is not affected. | + | // transmis constructorului nu este afectat. |
| | ||
book = null; | book = null; | ||
Line 140: | Line 140: | ||
} | } | ||
- | // Wind objects are instruments | + | // Obiectele Wind sunt instrumente |
- | // because they have the same interface: | + | // deoarece au aceeași interfață: |
public class Wind extends Instrument { | public class Wind extends Instrument { | ||
public static void main(String[] args) { | public static void main(String[] args) { | ||
Wind flute = new Wind(); | Wind flute = new Wind(); | ||
- | Instrument.tune(flute); // !! Upcasting automatically because the method | + | Instrument.tune(flute); // !! Upcasting automat pentru că metoda primește |
- | // gets an Instrument object, not a Wind object | + | // un obiect de tip Instrument, nu un obiect de tip Wind |
- | // So it would be redundant to make an explicit cast like | + | // Deci ar fi redundant să faci un cast explicit cum ar fi: |
// Instrument.tune((Instrument) flute) | // Instrument.tune((Instrument) flute) | ||
} | } | ||
Line 187: | Line 187: | ||
Animal a [] = new Animal[2]; | Animal a [] = new Animal[2]; | ||
| | ||
- | a[0] = new Wolf(); // Upcasting automatically | + | a[0] = new Wolf(); // Upcasting automat |
- | a[1] = new Snake(); // Upcasting automatically | + | a[1] = new Snake(); // Upcasting automat |
for (int i = 0; i < a.length; i++) { | for (int i = 0; i < a.length; i++) { | ||
Line 223: | Line 223: | ||
===Să încercăm să evităm folosirea instanceof=== | ===Să încercăm să evităm folosirea instanceof=== | ||
- | Totuși, deși v-am ilustrstat cum ''instanceof'' ne poate ajuta să ne dăm seama la ce să facem **downcasting**, este de preferat să ne organizăm clasele și designul codului în așa fel încât să lăsăm limbajul Java să facă automat verificarea tipului și să cheme metoda corespunzătoare. Vom refactororiza codul anterior pentru a nu fi nevoie de ''instanceof'': | + | Totuși, deși v-am ilustrat cum ''instanceof'' ne poate ajuta să ne dăm seama la ce să facem **downcasting**, este de preferat să ne organizăm clasele și designul codului în așa fel încât să lăsăm limbajul Java să facă automat verificarea tipului și să cheme metoda corespunzătoare. Vom refactororiza codul anterior pentru a nu fi nevoie de ''instanceof'': |
<code java5> | <code java5> | ||
Line 232: | Line 232: | ||
| | ||
public void action() { | public void action() { | ||
- | // we need this method because we will create a vector | + | // avem nevoie de această metodă deoarece vom crea un vector |
- | // of Animal instances and we will call this method on them | + | // cu instanțe Animal și vom apela această metodă pe ele |
} | } | ||
} | } | ||
class Wolf extends Animal { | class Wolf extends Animal { | ||
- | public void action() { // we call it howl in action | + | public void action() { |
System.out.println("Wolf howling"); | System.out.println("Wolf howling"); | ||
} | } | ||
Line 248: | Line 248: | ||
class Snake extends Animal { | class Snake extends Animal { | ||
- | public void action() { // we call it bite in action | + | public void action() { |
System.out.println("Snake biting"); | System.out.println("Snake biting"); | ||
} | } | ||
Line 263: | Line 263: | ||
a[i].eat(); | a[i].eat(); | ||
| | ||
- | // now that they are called the same, we can call the action method | + | // acum că ele sunt numite la fel, putem apela metoda action |
- | // from the Animal class (notice why it was a need to define the | + | // din clasa Animal (observați de ce a fost nevoie să definim |
- | // action method in the Animal class), and the appropriate method | + | // metoda action în clasa Animal), iar metoda corespunzătoare |
- | // will be called for the actual type of a[i] instance | + | // va fi apelată pentru tipul specific al instanței a[i] |
| | ||
a[i].action(); | a[i].action(); | ||
Line 345: | Line 345: | ||
Car d = null; | Car d = null; | ||
- | a.print(); // prints Car | + | a.print(); // afișează Car |
- | b.print(); // prints Dacia | + | b.print(); // afișează Dacia |
- | c.print(); // prints Dacia | + | c.print(); // afișează Dacia |
- | d.print(); // throws NullPointerException | + | d.print(); // aruncă NullPointerException |
</code> | </code> | ||
Line 359: | Line 359: | ||
Car d = null; | Car d = null; | ||
- | a.print(); // prints Car | + | a.print(); // afișează Car |
- | b.print(); // prints Car because the declared type of b is Car | + | b.print(); // afișează Car pentru că tipul dat la inițializare al lui b este Car |
- | c.print(); // prints Dacia because the declared type of c is Dacia | + | c.print(); // afișează Dacia pentru că tipul dat la inițializare al lui c este Dacia |
- | d.print(): // prints Car because the declared type of b is Car | + | d.print(): // afișează Car pentru că tipul dat la inițializare al lui b este Car |
</code> | </code> | ||
Line 368: | Line 368: | ||
<note warning>Sintaxa Java permite apelarea metodelor statice pe instanțe (e.g. a.print în loc de Car.print), dar acest lucru este considerat bad practice pentru că poate îngreuna înțelegerea codului.</note> | <note warning>Sintaxa Java permite apelarea metodelor statice pe instanțe (e.g. a.print în loc de Car.print), dar acest lucru este considerat bad practice pentru că poate îngreuna înțelegerea codului.</note> | ||
+ | |||
+ | ===Suprascrierea corecta a metodei equals(Object o)=== | ||
+ | |||
+ | Una din problemele cele mai des întâlnite este suprascrierea corectă a metodei //equals//. Mai jos putem vedea un exemplu de suprascriere incorectă a acestei metode. | ||
+ | |||
+ | <code java> | ||
+ | public class Car { | ||
+ | public boolean equals(Car c) { | ||
+ | System.out.println("Car"); | ||
+ | return true; | ||
+ | } | ||
+ | | ||
+ | public boolean equals(Object o) { | ||
+ | System.out.println("Object"); | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Prima metodă este o **supraîncărcare** a metodei equals iar a doua metodă este **suprascrierea** metodei equals. | ||
+ | |||
+ | <code java> | ||
+ | Car a = new Car(); | ||
+ | Dacia b = new Dacia(); | ||
+ | Int c = new Int(10); | ||
+ | |||
+ | a.print(); // afișează Car | ||
+ | b.print(); // afișează Car deoarece se face upcasting de la Dacia la Car | ||
+ | c.print(); // afișează Object deoarece se face upcasting de la Int la Object | ||
+ | </code> | ||
+ | |||
+ | Problema care se poate observa este că putem pasa ca argumente metodei equals si tipuri de date diferite de ''Car'', lucru ce ar putea arunca excepții de cast sau când vrem să accesăm anumite proprietăți din instanță. Mai jos este modul corect de suprascrie metoda equals. | ||
+ | |||
+ | <code java> | ||
+ | public class Car { | ||
+ | public boolean equals(Car c) | ||
+ | { | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | public boolean equals(Object o) | ||
+ | { | ||
+ | if (o == this) { | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | if (!(o instanceof Car)) { | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | return equals((Car) o); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | De reținut că folosirea ''instanceof'' nu este recomandată, însă în acest caz este singurul mod prin care ne putem asigura ca instanța de obiect trimisă metodei este de tip ''Car''. | ||
==Cuvântul cheie super. Întrebuințări== | ==Cuvântul cheie super. Întrebuințări== | ||
Line 387: | Line 443: | ||
// overrides printMethod in Superclass | // overrides printMethod in Superclass | ||
public void printMethod() { | public void printMethod() { | ||
- | super.printMethod(); // calls the parent method | + | super.printMethod(); // apelează metoda părinte |
| | ||
System.out.println("Printed in Subclass."); | System.out.println("Printed in Subclass."); | ||
Line 421: | Line 477: | ||
class Subclass extends Superclass { | class Subclass extends Superclass { | ||
public Subclass() { | public Subclass() { | ||
- | super(); // calls the parent constructor | + | super(); // apelează constructorul părinte |
- | // this call must be on the first line !! | + | // acest apel trebuie să fie pe prima linie a constructorului !! |
| | ||
System.out.println("Printed in Subclass constructor with no args."); | System.out.println("Printed in Subclass constructor with no args."); | ||
Line 428: | Line 484: | ||
| | ||
public Subclass(int a) { | public Subclass(int a) { | ||
- | super(a); // calls the parent constructor | + | super(a); // apelează constructorul părinte |
- | // this call must be on the first line !! | + | // acest apel trebuie să fie pe prima linie a constructorului !! |
| | ||
System.out.println("Printed in Subclass constructor with one integer argument."); | System.out.println("Printed in Subclass constructor with one integer argument."); | ||
Line 454: | Line 510: | ||
<note important>Chiar dacă nu se specifică apelul metodei ''super()'', compilatorul va apela automat constructor-ul implicit al părintelui însă dacă se dorește apelarea altui constructor, apelul de ''super(args)'' respectiv este obligatoriu</note> | <note important>Chiar dacă nu se specifică apelul metodei ''super()'', compilatorul va apela automat constructor-ul implicit al părintelui însă dacă se dorește apelarea altui constructor, apelul de ''super(args)'' respectiv este obligatoriu</note> | ||
| | ||
+ | ==Summary== | ||
+ | **Relații între obiecte:** | ||
+ | * Agregare - **has a** | ||
+ | * Moștenire - **is a** | ||
+ | |||
+ | **Upcasting** | ||
+ | * convertire **copil** => **parinte** | ||
+ | * realizată automat | ||
+ | |||
+ | **Downcasting** | ||
+ | * convertire **parinte** =>**copil** | ||
+ | * trebuie facută explicit de către programator | ||
+ | * încercați să evitați folosirea operatorului **instanceof** | ||
+ | |||
+ | **Suprascrierea** | ||
+ | * înlocuirea functionalitații metodei din clasa de bază în clasa derivată | ||
+ | * pastreaza numele și semnatura metodei | ||
+ | |||
+ | **Supraincarcarea** | ||
+ | * în interiorul clasei pot exista mai multe metode cu acelasi nume, cu condiția ca semnătura (tipul, argumentele) să fie diferită | ||
+ | |||
+ | **super** | ||
+ | * instanța clasei parinte | ||
+ | * amintiți-vă din laboratorul anterior că **[[[[laboratoare:constructori-referinte|this]]** se referă la instanța clasei curente | ||
+ | |||
+ | |||
==Exerciții== | ==Exerciții== | ||
- | <note important>Exercițiile 1-5 se rezolvă în ordine</note> | + | Gigel vrea să-i faca mamei sale un cadou de ziua ei și știe că-i plac foarte mult bomboanele. El are nevoie de ajutorul vostru pentru a construi cel mai frumos și gustos cadou: |
+ | |||
+ | **Task 1** [2p] | ||
- | - (**2p**) Întrucât în ierarhia de clase Java, clasa ''Object'' se află în rădăcina arborelui de moștenire pentru orice clasă, orice clasă va avea acces la o serie de facilități oferite de ''Object''. Una dintre ele este metoda ''toString()'', al cărei scop este de a oferi o reprezentare a unei instanțe de clasă sub forma unui șir de caractere. | + | Veti proiecta o clasa ''CandyBox'', care va conține câmpurile private ''flavor'' (String) și ''origin'' (String). Clasa va avea, de asemenea: |
- | * Definiți clasa **''Form''** cu un membru ''color'' de tip ''String'', o metoda ''getArea()'' care pentru început va intoarce 0 și o metodă ''toString()'' care va returna acestă culoare. | + | * un constructor fără parametri |
- | * Clasa va avea, de asemenea: | + | * un constructor ce va inițializa toate campurile |
- | * un constructor fără parametri | + | * o metoda ''getVolume()'', care va intoarce valoarea 0; |
- | * un constructor ce va inițializa culoarea. | + | * Întrucât clasa ''Object'' se află în rădăcina arborelui de moștenire pentru orice clasă, orice instanta va avea acces la o serie de facilități oferite de Object. Una dintre ele este metoda ''toString()'', al cărei scop este de a oferi o reprezentare unei instanțe sub forma unui șir de caractere, utilizata in momentul apelului System.out.println(). Adaugati o metoda ''toString()'', care va returna flavor-ul si regiunea de proveniență a cutiei de bomboane. |
- | * Din ea derivați clasele **''Triangle''** și **''Circle''**: | + | |
- | * Clasa ''Triangle'' va avea 2 membri ''height'' si ''base'' de tip ''float''. | + | |
- | * Clasa ''Circle'' va avea membrul ''radius'' de tip ''float''. | + | |
- | * Clasele vor avea: | + | |
- | * constructori fără parametri | + | |
- | * constructori care permit inițializarea membrilor. Identificați o modalitate de **reutilizare** a codului existent. | + | |
- | * Instanțiati clasele ''Triangle'' și ''Circle'', și apelați metoda ''toString()'' pentru fiecare instanță. | + | |
- | * suprascrieti metoda ''getArea()'' pentru a intoarce aria specifica figuri geometrice. | + | |
- | - (**2p**) Adăugați metode ''toString()'' în cele două clase derivate, care să returneze tipul obiectului, culoarea si aria. De exemplu: | + | |
- | * pentru clasa ''Triangle'', se va afișa: "Triunghi: rosu 10" | + | |
- | * pentru clasa ''Circle'', se va afișa: "Cerc: verde 12.56" | + | |
- | * Modificați implementarea ''toString()'' din clasele derivate astfel încât aceasta să utilizeze implementarea metodei ''toString()'' din clasa de bază. | + | |
- | - (**1p**) Adăugați o metodă ''equals'' în clasa ''Triangle''. Justificați criteriul de echivalentă ales. | + | |
- | * Hint: vedeți metodele clasei [[http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html | Object]], moștenită de toate clasele - Object are metoda ''equals'', a cărei implementare verifică echivalența obiectelor comparând referințele. | + | |
- | - (**1p**) Upcasting. | + | |
- | * Creați un vector de obiecte ''Form'' și populați-l cu obiecte de tip ''Triangle'' și ''Circle'' (upcasting). | + | |
- | * Parcurgeți acest vector și apelați metoda ''toString()'' pentru elementele sale. Ce observați? | + | |
- | - (**2p**) Downcasting. | + | |
- | * Adăugați clasei ''Triangle'' metoda ''printTriangleDimensions'' și clasei ''Circle'' metoda ''printCircleDimensions''. Implementarea metodelor constă în afișarea bazei si inaltimii respectiv razei. | + | |
- | * Parcurgeți vectorul de la exercițiul anterior și, folosind downcasting la clasa corespunzătoare, apelați metodele specifice fiecărei clase (''printTriangleDimensions'' pentru ''Triangle'' și ''printCircleDimensions'' pentru ''Circle''). Pentru a stabili tipul obiectului curent folosiți operatorul **''instanceof''**. | + | |
- | * Modificați programul anterior astfel încât downcast-ul să se facă mereu la clasa ''Triangle''. Ce observați? | + | |
- | * Modificați programul anterior astfel încât să nu mai aveți nevoie de ''instanceof'' deloc. | + | |
- | <note important>Exercițiul 6 este independent de cele de mai sus</note> | + | **Task 2** [2p] |
- | - (**1.5p + 1.5p**) Implementați două clase ''QueueAggregation'' și ''QueueInheritance'' pe baza clasei ''{{:laboratoare:agregare-mostenire:array.zip | Array}}'' furnizate de noi, utilizând, pe rând, ambele abordări: **moștenire** și **agregare**. Precizări: | + | Din ea derivați clasele ''Lindt'', ''Chococo'', ''Baravelli'', ''ChocAmor''. Pentru un design interesant, cutiile vor avea forme diferite: |
- | * Coada va conține elemente de tip ''int''. | + | * //Lindt// va contine ''length'', ''width'', ''height'' (float); |
- | * Clasele ''QueueAggregation'' și ''QueueInheritance'' trebuie să ofere metodele ''enqueue'' și ''dequeue'', specifice acestei structuri de date. | + | * //Chococo// va avea forma unui cilindru cu baza triunghilara, cu campurile ''baseHeight'', ''height'' si ''base'' (float) |
- | * Clasa ''Array'' reprezintă un wrapper pentru lucrul cu vectori. Metoda ''get(pos)'' întoarce valoarea din vector de la poziția ''pos'', în timp ce metoda ''set(pos, val)'' atribuie poziției ''pos'' din vector valoarea ''val''. Noutatea constă în verificarea poziției furnizate. În cazul în care aceasta nu se încadrează în intervalul valid de indici, ambele metode întorc constanta ''ERROR'' definită în clasa. | + | * //Baravelli// va fi un cilindru. Acesta va conține un camp ''radius'' și unul ''height'' (float); |
- | * Metoda ''main'' definită în clasa ''Array'' conține exemple de utilizare a acestei clase. Experimentați! | + | * //ChocAmor//, fiind un cub, va conține un camp ''length'' (float); |
- | * Metoda ''enqueue'' va oferi posibilitatea introducerii unui număr întreg în capătul cozii (dacă aceasta nu este deja plină), în timp ce metoda ''dequeue'' va înlătura elementul din vârful cozii și îl va întoarce (dacă coada nu este goală). În caz de insucces (coada plină la ''enqueue'', respectiv goală la ''dequeue''), ambele metode vor întoarce constanta ''ERROR''. | + | |
- | * Ce problemă poate apărea din cauza constantei ''ERROR''? (Hint: Dacă în coadă am un element egal cu valoarea constantei ''ERROR''?) Gândiți-vă la o rezolvare. | + | |
- | * Ce puteți spune despre vizibilitatea metodelor ''get'' și ''set'', în clasele ''QueueAggregation'' și ''QueueInheritance'', în varianta ce utilizează moștenire? Ce problemă indică răspunsul? Furnizați o soluție la această problemă. | + | |
+ | Clasele vor avea: | ||
+ | * constructori fără parametri | ||
+ | * constructori care permit inițializarea membrilor. Identificați o modalitate de reutilizare a codului existent. Pentru fiecare tip de cutie veti initializa, in constructor, campurile ''flavor'' și ''origin'' cu tipul corespunzator | ||
+ | * Suprascrieti metoda //getVolume()// pentru a intoarce volumul specific fiecarei cutii de bomboane, in functie de tipul sau. | ||
+ | * Suprascrieti metoda //toString()// în clasele derivate, astfel încat aceasta să utilizeze implementarea metodei toString() din clasa de bază. Returnați un mesaj de forma //"The " + origin + " " + flavor + " has volume " + volume//; | ||
- | == Resurse == | + | **Task 3** [1p] |
- | * {{:laboratoare:agregare-mostenire:array.zip|Arhiva zip cu clasa Array.java}} | + | |
- | * <html><a class="media mediafile mf_pdf" href="?do=export_pdf">PDF laborator</a></html> | + | |
- | * **{{:laboratoare:agregare-mostenire:agregare-mostenire-sol.zip | Soluție}}** | + | |
+ | Adăugați o metodă ''equals()'' în clasa ''CandyBar''. Justificați criteriul de echivalentă ales. Vedeți metodele clasei [[https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Object.html | Object]], moștenită de toate clasele - Object are metoda equals, a cărei implementare verifică echivalența obiectelor comparând referințele. | ||
+ | <note important>**Hint:** | ||
+ | Puteti genera automat metoda, cu ajutorul IDE. Selectați câmpurile considerate și | ||
+ | analizați în ce fel va fi suprascrisă metoda equals.</note> | ||
+ | |||
+ | **Task 4** - //''Upcasting''// [2p] | ||
+ | |||
+ | Acum că am stabilit tipul cutiilor de bomboane, putem construi cadoul, ramanand la latitudinea vostra care va fi designul lui. In pachetul java.util se gaseste clasa ''ArrayList'', care definește un resizable array, cu metodele specifice (add, size, get, lista lor completa este in [[https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/ArrayList.html|documentatie]]). Creati o clasă ''CandyBag'', care va conține un ArrayList cu mai multe cutii din fiecare tip. Creați obiecte de tip Chococo si testați egalitatea lor; | ||
+ | |||
+ | |||
+ | **Task 5** - //''Downcasting''// [1p] | ||
+ | |||
+ | Adaugati clasei ''Baravelli'', functia ''printBaravelliDim()'', care va afișa dimensiunile razei și inaltimii. În mod analog, procedati cu celelalte tipuri de cutii, adaugand metodele ''printChococoDim()'', ''printChocAmorDim()'' si ''printLindtDim()'', în care să afișați dimensiunile fiecarei cutii. | ||
+ | |||
+ | **Task 6** - //''Agregare''// [2p] | ||
+ | |||
+ | Gigel va vrea sa trimită prin curier cadoul, pentru a nu-l gasi mama lui mai devreme. Ajutați-l să determine locația, creând clasa "Area", care va conține un obiect de tip ''CandyBag'', un camp "number" (int) și un câmp "street" (String) | ||
+ | Clasa va avea, de asemenea: | ||
+ | * un constructor fără parametri | ||
+ | * un constructor ce va inițializa toate campurile | ||
+ | * Acum ca am finalizat construcția, îi vom oferi mamei informații despre cadoul ei printr-o felicitare. Creați o metoda ''getBirthdayCard()'', care va afișa, pe primul rand, adresa completă, iar apoi un mesaj de ''la multi ani''. | ||
+ | * Tot aici, parcurgeți array-ul, apeland metoda ''toString()'' pentru elementele sale. | ||
+ | * Parcurgeți array-ul și, folosind downcasting la clasa corespunzătoare, apelați metodele specifice fiecărei clase. Pentru a stabili tipul obiectului curent folosiți operatorul ''instanceof'' | ||
+ | * In final, modificați cum considerati programul anterior astfel încât să nu mai aveți nevoie de instanceof. | ||
+ | |||
+ | == Resurse == | ||
+ | * {{|Soluție}} (disponibilă din 28.10.2019) | ||
+ | * [[laboratoare:old-exercises|Exerciții din alți ani]] | ||
== Referințe == | == Referințe == |