This shows you the differences between two versions of the page.
|
poo-ca-cd:laboratoare:clase-interne-si-strings [2025/11/05 03:17] florian_luis.micu [Utilizarea claselor interne] |
poo-ca-cd:laboratoare:clase-interne-si-strings [2025/11/06 14:02] (current) florian_luis.micu [Laboratorul 5: Clase Interne și Strings] |
||
|---|---|---|---|
| Line 3: | Line 3: | ||
| * Autori: [[stefancocioran@gmail.com | Ștefan Cocioran ]], [[miculuis1@gmail.com | Florian-Luis Micu ]], [[sorinabuf@gmail.com | Sorina-Anamaria Buf ]] | * Autori: [[stefancocioran@gmail.com | Ștefan Cocioran ]], [[miculuis1@gmail.com | Florian-Luis Micu ]], [[sorinabuf@gmail.com | Sorina-Anamaria Buf ]] | ||
| * Data publicării: 03.11.2025 | * Data publicării: 03.11.2025 | ||
| - | * Data ultimei modificări: 05.11.2025 | + | * Data ultimei modificări: 06.11.2025 |
| * ștergerea notiței legată de contest (ambele observații au fost rezolvate). | * ștergerea notiței legată de contest (ambele observații au fost rezolvate). | ||
| - | * refrazări pentru a favoriza o lectură clară și rapidă. | + | * refrazări pentru favorizarea unei lecturi clare și rapide. |
| + | * indicarea că o clasă internă poate fi record, enum, interfață sau clasă abstractă. | ||
| + | * modificări pentru secțiunea clase anonime | ||
| + | * adăugarea unui exemplu mai detaliat pentru clase anonime în GUI. | ||
| + | * precizarea că o clasă internă are acces la membrii privați ai clasei externe. | ||
| + | * adăugarea unor secțiuni legate de accesul la metode și variabile în mai multe contexte. | ||
| ===== Obiective ===== | ===== Obiective ===== | ||
| Line 43: | Line 48: | ||
| * clase interne metodelor (//method-local inner classes//) sau blocurilor | * clase interne metodelor (//method-local inner classes//) sau blocurilor | ||
| - | <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 vs. Inner classes]] | ||
| - | </note> | ||
| ====Clase interne "normale" ==== | ====Clase interne "normale" ==== | ||
| Line 100: | Line 99: | ||
| Car.class Car$OttoEngine.class Engine.class Test.class Test.java | Car.class Car$OttoEngine.class Engine.class Test.class Test.java | ||
| </code> | </code> | ||
| - | |||
| - | Urmăriți exemplul de folosire a claselor interne de mai sus. Adresați-vă asistentului pentru eventuale neclarități. | ||
| Din interiorul unei clase interne, putem accesa **referința la instanța clasei externe** folosind numele acesteia urmat de keyword-ul ''this'': | Din interiorul unei clase interne, putem accesa **referința la instanța clasei externe** folosind numele acesteia urmat de keyword-ul ''this'': | ||
| <code java> | <code java> | ||
| - | Car.this; | + | class Car { |
| + | private String model = "Tesla"; | ||
| + | |||
| + | class OttoEngine { | ||
| + | private int fuelCapacity; | ||
| + | |||
| + | public OttoEngine(int fuelCapacity) { | ||
| + | this.fuelCapacity = fuelCapacity; | ||
| + | } | ||
| + | |||
| + | public void printCarModel() { | ||
| + | // Accesăm instanța clasei exterioare folosind Car.this | ||
| + | System.out.println("Car model: " + Car.this.model); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | public OttoEngine getEngine() { | ||
| + | return new OttoEngine(11); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | public class TestThis { | ||
| + | public static void main(String[] args) { | ||
| + | Car car = new Car(); | ||
| + | Car.OttoEngine engine = car.getEngine(); | ||
| + | engine.printCarModel(); // Va afișa: Car model: Tesla | ||
| + | } | ||
| + | } | ||
| </code> | </code> | ||
| <note tip> | <note tip> | ||
| - | Țineți cont de faptul că o **clasă internă** se comportă ca un **membru al clasei**, concret **nu** puteți folosi un membru al clasei dacă nu inițializați mai întâi clasa. | + | * Țineți cont de faptul că o **clasă internă** se comportă ca un **membru al clasei**, concret **nu** puteți folosi un membru al clasei dacă nu inițializați mai întâi clasa. |
| + | * Chiar dacă ''model'' este un **câmp privat**, clasa internă **îl poate accesa**, deoarece și clasa internă este un membru al clasei. | ||
| + | * O clasă internă nu trebuie neapărat să fie o clasă obișnuită; puteți declara ca membri interni și **records**, **enums**, **interfețe sau clase abstracte**. | ||
| </note> | </note> | ||
| Line 204: | Line 230: | ||
| * Clasa este **anonimă**, adică nu are nume, dar implementează tipul ''Engine'', deci poate defini metodele interfeței. | * Clasa este **anonimă**, adică nu are nume, dar implementează tipul ''Engine'', deci poate defini metodele interfeței. | ||
| * Corpul clasei apare **imediat după instanțiere**, între ''{ ... }''. | * Corpul clasei apare **imediat după instanțiere**, între ''{ ... }''. | ||
| + | </note> | ||
| + | |||
| + | <note tip> | ||
| + | Clasa moștenită poate să fie o **interfață**, o **clasă abstractă** sau o **clasă normală**. | ||
| </note> | </note> | ||
| Line 213: | Line 243: | ||
| | **Durata de viață** | Obiectele anonime sunt create și folosite pe loc, deci potrivite pentru scopuri scurte. | | | **Durata de viață** | Obiectele anonime sunt create și folosite pe loc, deci potrivite pentru scopuri scurte. | | ||
| - | ===Accesul la variabile locale=== | + | ===Accesul la variabile și metode=== |
| - | O clasă internă anonimă declarată într-o metodă **poate folosi variabilele locale** și parametrii acelei metode doar dacă aceștia sunt: | + | Față de clasele interne normale, clasele anonime au câteva restricții în ceea ce privește accesul la variabile și metode în funcție de locația acestora. |
| + | |||
| + | ==Accesul la câmpurile clasei externe== | ||
| + | |||
| + | Câmpurile clasei externe pot fi accesate din interiorul clasei anonime **fără** să fie nevoie să se impună anumite restricții. | ||
| + | |||
| + | <code java> | ||
| + | public class Outer { | ||
| + | private String secret = "Private info"; | ||
| + | |||
| + | interface Greeter { | ||
| + | void greet(); | ||
| + | } | ||
| + | |||
| + | public void createAnonymous() { | ||
| + | Greeter g = new Greeter() { | ||
| + | @Override | ||
| + | public void greet() { | ||
| + | // acces direct la câmpul privat al clasei externe | ||
| + | System.out.println("Secretul este: " + secret); | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | g.greet(); // -> "Secretul este: Private info" | ||
| + | } | ||
| + | |||
| + | public static void main(String[] args) { | ||
| + | Outer outer = new Outer(); | ||
| + | outer.createAnonymous(); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ==Accesul la câmpuri moștenite de clasa anonimă== | ||
| + | |||
| + | Dacă clasa anonimă extinde o **clasă normală** sau **abstractă**, ea primește toate câmpurile (''public''/''protected''/''package-private'' sau ''private'' prin getteri) ale clasei părinte: | ||
| + | |||
| + | <code java> | ||
| + | abstract class Animal { | ||
| + | protected int age = 5; // câmp moștenit | ||
| + | |||
| + | public abstract void showDetails(); | ||
| + | } | ||
| + | |||
| + | public class Test { | ||
| + | public static void main(String[] args) { | ||
| + | Animal a = new Animal() { | ||
| + | @Override | ||
| + | public void showDetails() { | ||
| + | System.out.println("Animal has age: " + age); // folosește câmpul moștenit | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | a.showDetails(); // Output: "Animal has age 5" | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Dacă clasa anonimă implementează o **interfață**, interfața nu are câmpuri ale instanței, doar **constante** (declarate ''public static final'') care pot fi accesate direct din clasa anonimă. | ||
| + | |||
| + | <code java> | ||
| + | interface Greeter { | ||
| + | String NAME = "Ana"; // constantă | ||
| + | |||
| + | void greet(); | ||
| + | } | ||
| + | |||
| + | public class Main { | ||
| + | public static void main(String[] args) { | ||
| + | Greeter g = new Greeter() { | ||
| + | @Override | ||
| + | public void greet() { | ||
| + | // Accesăm direct constanta din interfața implementată | ||
| + | System.out.println("Salut, " + NAME); | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | g.greet(); // Output: Salut, Ana | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ==Accesul la metode== | ||
| + | |||
| + | - **Metode moștenite din clasa părinte** | ||
| + | * Clasele anonime **pot folosi și suprascrie metodele clasei părinte**. | ||
| + | * Se pot apela aceste metode **direct** din corpul clasei anonime. | ||
| + | * **Metodele abstracte trebuie implementate obligatoriu** în clasa anonimă <code java> | ||
| + | class Animal { | ||
| + | protected int age = 5; | ||
| + | |||
| + | public void speak() { | ||
| + | System.out.println("Animal speaks"); | ||
| + | } | ||
| + | |||
| + | public abstract void showDetails(); | ||
| + | } | ||
| + | |||
| + | public class Test { | ||
| + | public static void main(String[] args) { | ||
| + | Animal a = new Animal() { | ||
| + | // Implementăm metoda abstractă | ||
| + | @Override | ||
| + | public void showDetails() { | ||
| + | speak(); // folosește metoda moștenită | ||
| + | System.out.println("Animal has age: " + age); | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | a.showDetails(); | ||
| + | a.speak(); // o putem apela și direct pentru că referința este de tip Animal | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | - **Metode noi definite în clasa anonimă** | ||
| + | * Se pot adăuga metode proprii, dar **nu se pot accesa prin referința superclasei/interfeței**. | ||
| + | * Sunt vizibile **doar** în interiorul clasei anonime, deci pot fi considerate metode private sau helper. <code java> | ||
| + | class Animal { | ||
| + | protected int age = 5; | ||
| + | |||
| + | public void speak() { | ||
| + | System.out.println("Animal speaks"); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | public class Test { | ||
| + | public static void main(String[] args) { | ||
| + | Animal a = new Animal() { | ||
| + | // Declarăm o metodă proprie | ||
| + | public void showDetails() { | ||
| + | System.out.println("Age: " + age); | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | a.showDetails(); // nu va funcționa pentru că referința este de tip Animal | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | - **Metode statice** | ||
| + | * **Nu putem defini metode statice** într-o clasă anonimă, deoarece acestea nu au nume, deci nu am avea cum să le apelăm. | ||
| + | |||
| + | <note important> | ||
| + | * Atunci când suprascriem o metodă, **metoda apelată la run-time este determinată de instanța efectivă**, nu de tipul referinței. În exemplele cu clase anonime, referința pare de tip ''Animal'', dar JVM creează pentru fiecare clasa anonimă un **nume intern unic**, astfel încât suprascrierea să funcționeze corect. | ||
| + | * Metodele definite exclusiv în clasele anonime **nu pot fi apelate din afara acestora**, deoarece compilatorul verifică tipul referinței pentru a determina ce metode există. Deși JVM alocă fiecărei clase anonime un **ID intern** la run-time, acesta nu este cunoscut la compilare, deci nu putem declara o referință care să aibă tipul clasei anonime. | ||
| + | </note> | ||
| + | |||
| + | ==Accesul la variabile locale metodei== | ||
| + | |||
| + | O clasă internă anonimă declarată într-o metodă **poate folosi variabilele locale** și **parametrii** acelei metode doar dacă aceștia sunt: | ||
| * declarați ''final'', sau | * declarați ''final'', sau | ||
| * sunt **effectively final**, adică nu își schimbă valoarea după inițializare. | * sunt **effectively final**, adică nu își schimbă valoarea după inițializare. | ||
| - | Variabilele locale sunt stocate pe **stivă**, deci dispar după terminarea metodei. Pentru a fi accesibile în clasa internă, Java creează **o copie** a acestor variabile și o stochează ca **atribut intern** al clasei anonime. | + | <code java> |
| + | class Car { | ||
| + | interface Engine { int getFuelCapacity(); } | ||
| + | public Engine getEngine(int fuelCapacityParam) { | ||
| + | int baseCapacity = fuelCapacityParam; // effectively final | ||
| + | | ||
| + | return new Engine() { | ||
| + | public int getFuelCapacity() { | ||
| + | return baseCapacity + 5; // poate fi folosit | ||
| + | } | ||
| + | }; | ||
| + | } | ||
| + | |||
| + | public static void main(String[] args) { | ||
| + | Car car = new Car(); | ||
| + | System.out.println(car.getEngine(10).getFuelCapacity()); // 15 | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Variabilele locale sunt stocate pe **stivă**, deci dispar după terminarea metodei. Pentru a fi accesibile în clasa internă, Java creează **o copie** a acestor variabile și o stochează ca **atribut intern** al clasei anonime. | ||
| <note important> | <note important> | ||
| Line 279: | Line 477: | ||
| * Vom studia expresiile lambda în detaliu în laboratoarele dedicate c**olecțiilor și stream-urilor**. | * Vom studia expresiile lambda în detaliu în laboratoarele dedicate c**olecțiilor și stream-urilor**. | ||
| </note> | </note> | ||
| - | |||
| - | |||
| - | <hidden> | ||
| - | |||
| - | **Mutat în laboratorul despre streams** | ||
| - | |||
| - | **[Nice to know] Expresii Lambda continuare** | ||
| - | |||
| - | O altă situație des intalnită de folosire a lambda-urilor este pentru **transmiterea de funcții ca parametru** iar api-uri precum cel de filtrare al colecțiilor le utilizează intens ([[https://www.baeldung.com/java-stream-filter-lambda|exemplu]]). | ||
| - | |||
| - | |||
| - | În exemplul de mai jos aveți niște exemple simple de folosire lambda ca paremetru pentru metode. Parametrii primiți în lambda pot fi zero (//()// ca în exemplul cu fuelCapacity) sau mai mulți (param1, param2, ...). | ||
| - | <code java> | ||
| - | public static void main(String[] args) { | ||
| - | | ||
| - | ArrayList<Integer> values = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); | ||
| - | |||
| - | values.removeIf((v) -> v % 2 == 0); // parametru o lambda care intoarce daca numărul primit este par sau nu | ||
| - | |||
| - | values.forEach((v) -> System.out.println(v)); // parametru o lambda care afiseaza argumentul primit | ||
| - | values.forEach(System.out::println); // referinta catre metoda println | ||
| - | } | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | **Operatorul [[https://www.geeksforgeeks.org/double-colon-operator-in-java/|::]]** este folosit pentru referințierea metodelor. | ||
| - | |||
| - | Putem folosi funcții anonime pentru a executa diverse **operații pe liste** (de exemplu ''removeIf'', care filtrează elementele unei colecții pe baza unui predicat, și ''replaceAll'', care aplică o operație pe toate elementele unei colecții). | ||
| - | |||
| - | Exemple: | ||
| - | <code java> | ||
| - | List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); | ||
| - | |||
| - | // incrementează toate numerele din colecție cu 1 | ||
| - | list.replaceAll((x) -> x + 1); | ||
| - | |||
| - | // șterge din colecție numerele impare | ||
| - | list.removeIf((x) -> x % 2 == 1); | ||
| - | </code> | ||
| - | |||
| - | O altă utilitate a funcțiilor anonime reprezintă în **implementarea comparatorilor** folosiți la sortare sau la crearea de colecții sortate (TreeSet, TreeMap). | ||
| - | |||
| - | Exemple: | ||
| - | <code java> | ||
| - | // o variantă | ||
| - | Collections.sort(list, (o1, o2) -> o2 - o1); | ||
| - | |||
| - | // alta variantă, prin care se folosim de metoda sort() din interfața List | ||
| - | list.sort((o1, o2) -> o2 - o1); | ||
| - | |||
| - | // colecții sortate | ||
| - | TreeSet<Integer> sortedSet = new TreeSet<>((o1, o2) -> o1 - o2); | ||
| - | TreeMap<Integer, Integer> sortedMap = new TreeMap<>((o1, o2) -> o1 - o2); | ||
| - | </code> | ||
| - | |||
| - | </hidden> | ||
| - | |||
| ====Clase interne statice==== | ====Clase interne statice==== | ||
| Line 536: | Line 677: | ||
| <code java> | <code java> | ||
| - | button.addActionListener(new ActionListener() { // clasa anonimă implementează ActionListener | + | // Codul tău cu clasa anonimă |
| - | public void actionPerformed(ActionEvent e) { | + | public class MyGUI { |
| - | numClicks++; | + | |
| + | public void closeWindow() { | ||
| + | // Clasa anonimă implementează ActionListener | ||
| + | button.addActionListener(new ActionListener() { | ||
| + | @Override | ||
| + | public void actionPerformed(ActionEvent e) { | ||
| + | numClicks++; | ||
| + | } | ||
| + | }); | ||
| } | } | ||
| - | }); | + | } |
| + | |||
| + | // Cum ar putea arăta metoda addActionListener intern | ||
| + | class JButton { | ||
| + | ... | ||
| + | |||
| + | public void addActionListener(ActionListener listener) { | ||
| + | // apelează metoda suprascrisă din clasa anonimă | ||
| + | listener.actionPerformed(this.event); | ||
| + | } | ||
| + | |||
| + | ... | ||
| + | } | ||
| </code> | </code> | ||
| <note tip> | <note tip> | ||
| - | În acest exemplu, clasa anonimă implementează ''ActionListener'' și suprascrie metoda ''actionPerformed'', iar implementarea rămâne ascunsă de restul aplicației. | + | * În acest exemplu, clasa anonimă implementează ''ActionListener'' și suprascrie metoda ''actionPerformed'', iar implementarea **rămâne ascunsă** de restul aplicației. |
| + | * Exemplul de mai sus folosește o **versiune simplificată** a clasei ''JButton'' pentru a ilustra exact de ce este nevoie să **pasăm o clasă ca argument** și cum putem **scurta codul folosind o clasă anonimă** pentru acest caz. | ||
| </note> | </note> | ||
| Line 941: | Line 1103: | ||
| </note> | </note> | ||
| - | ====[Nice to know] Cǎutarea în conținutul unui String==== | + | ====[Nice to know] Căutarea în conținutul unui String==== |
| Clasa String oferă mai multe metode simple pentru a căuta subșiruri fixe într-un șir. Astfel, metodele **startsWith()** și **endsWith()** verifică dacă șirul începe sau se termină cu un anumit subșir: | Clasa String oferă mai multe metode simple pentru a căuta subșiruri fixe într-un șir. Astfel, metodele **startsWith()** și **endsWith()** verifică dacă șirul începe sau se termină cu un anumit subșir: | ||
| Line 968: | Line 1130: | ||
| Pentru căutări complexe în șiruri, Java oferă API-ul pentru expresii regulate ([[https://docs.oracle.com/javase/tutorial/essential/regex/|Regular Expressions]]), care utilizează sintaxa RegEx pentru definirea și identificarea tiparelor în text. | Pentru căutări complexe în șiruri, Java oferă API-ul pentru expresii regulate ([[https://docs.oracle.com/javase/tutorial/essential/regex/|Regular Expressions]]), care utilizează sintaxa RegEx pentru definirea și identificarea tiparelor în text. | ||
| - | <spoiler [Optional] Metodele clasei String> | + | <spoiler Metodele clasei String> |
| În Java, clasa String oferă o varietate de metode care permit manipularea și analiza șirurilor de caractere. Mai jos prezentăm câteva metode importante, dar pentru detalii suplimentare consultați [[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html|documentația oficială Oracle]]. | În Java, clasa String oferă o varietate de metode care permit manipularea și analiza șirurilor de caractere. Mai jos prezentăm câteva metode importante, dar pentru detalii suplimentare consultați [[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html|documentația oficială Oracle]]. | ||
| Line 1256: | Line 1418: | ||
| ===== Resurse și link-uri utile ===== | ===== Resurse și link-uri utile ===== | ||
| + | |||
| + | * [[https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html#:~:text=A%20nested%20class%20is%20a,members%20of%20the%20enclosing%20class.|Nested vs Inner classes]] | ||
| * [[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html|String - documentația oficială Oracle]] | * [[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html|String - documentația oficială Oracle]] | ||
| - | * [[:poo-ca-cd:arhiva:laboratoare:2024:java-features#interfete_functionale_functii_si_expresii_lambda|Expresii Lambda - Arhivǎ]] | + | * [[https://www.baeldung.com/java-string-pool|Guide to Java String Pool - Baeldung]] |
| - | * [[:poo-ca-cd:laboratoare:old-exercises#clase-interne| Old exercises]] | + | * [[https://coderpad.io/blog/development/the-complete-guide-to-regular-expressions-regex/|The complete guide to Regular Expressions - CoderPad]] |