Ce face Generics (tipuri generice)
List<String> înseamnă „listă de șiruri”. Parametrul de tip între < > spune compilatorului ce conține colecția: add(42) nu compilează, iar get(0) returnează direct un String, fără conversie. Operatorul diamant new ArrayList<>() deduce tipul din stânga.
Poți scrie clase generice (class Cutie<T>) și metode generice (static <T> T primul(List<T> l)). Cu <T extends Comparable<T>> limitezi tipul la cele care se pot compara. Parametrii de tip se scriu de obicei cu o literă mare: T (tip), E (element), K/V (cheie/valoare).
Wildcard-ul ? face metodele mai flexibile: List<? extends Number> acceptă List<Integer> și List<Double> (doar citești din ea), iar List<? super Integer> acceptă liste în care poți adăuga întregi. Generics nu funcționează cu tipuri primitive: scrii List<Integer>, nu List<int>. La rulare informația de tip se șterge (type erasure).
Exemple
Exemplul 1: O clasă generică
Pereche<A, B> ține două valori de tipuri oarecare. Același cod servește pentru Pereche<String, Integer> și Pereche<String, Double>, iar getDoi() returnează tipul corect, fără conversie.
class Pereche<A, B> {
private final A unu;
private final B doi;
Pereche(A unu, B doi) {
this.unu = unu;
this.doi = doi;
}
A getUnu() { return unu; }
B getDoi() { return doi; }
<C> Pereche<A, C> cuDoi(C nou) {
return new Pereche<>(unu, nou);
}
@Override
public String toString() {
return "(" + unu + ", " + doi + ")";
}
}
public class Main {
public static void main(String[] args) {
Pereche<String, Integer> varsta = new Pereche<>("Ana", 21);
int ani = varsta.getDoi(); // fără cast
System.out.println(varsta + " -> peste 5 ani: " + (ani + 5));
Pereche<String, Double> medie = varsta.cuDoi(9.45);
System.out.println(medie + " -> " + medie.getDoi().getClass().getSimpleName());
}
}
Rezultat
(Ana, 21) -> peste 5 ani: 26
(Ana, 9.45) -> Double
Exemplul 2: Metode generice cu limită (extends)
maxim merge pentru orice tip comparabil: numere, texte, date. Tipul T este dedus din argument. Limita este Comparable<? super T>, nu Comparable<T>: LocalDate implementează Comparable<ChronoLocalDate> (un supertip), iar cu limita simplă apelul nu compilează (inference variable T has incompatible equality constraints).
import java.time.LocalDate;
import java.util.List;
public class Main {
static <T extends Comparable<? super T>> T maxim(List<T> elemente) {
T max = elemente.get(0);
for (T e : elemente) {
if (e.compareTo(max) > 0) {
max = e;
}
}
return max;
}
static <T> void afiseaza(String eticheta, List<T> lista) {
System.out.println(eticheta + " (" + lista.size() + "): " + lista);
}
public static void main(String[] args) {
List<Integer> note = List.of(7, 10, 8, 9);
List<String> orase = List.of("Iași", "Arad", "Zalău", "Brăila");
List<LocalDate> termene = List.of(LocalDate.of(2026, 11, 3), LocalDate.of(2026, 12, 20));
afiseaza("note", note);
System.out.println("nota maximă: " + maxim(note));
System.out.println("ultimul oraș alfabetic: " + maxim(orase));
System.out.println("ultimul termen: " + maxim(termene));
}
}
Rezultat
note (4): [7, 10, 8, 9]
nota maximă: 10
ultimul oraș alfabetic: Zalău
ultimul termen: 2026-12-20
Exemplul 3: Wildcard: ? extends și ? super
suma citește din orice listă de numere. umple scrie întregi în orice listă care îi poate primi (Integer, Number, Object).
import java.util.ArrayList;
import java.util.List;
public class Main {
static double suma(List<? extends Number> numere) {
double s = 0;
for (Number n : numere) {
s += n.doubleValue();
}
return s;
}
static void umple(List<? super Integer> destinatie, int pana) {
for (int i = 1; i <= pana; i++) {
destinatie.add(i);
}
}
public static void main(String[] args) {
List<Integer> bucati = List.of(3, 5, 2);
List<Double> preturi = List.of(12.5, 7.25);
System.out.println("bucăți: " + suma(bucati));
System.out.println("prețuri: " + suma(preturi));
List<Number> amestec = new ArrayList<>();
amestec.add(0.5);
umple(amestec, 3);
System.out.println("amestec: " + amestec + ", sumă: " + suma(amestec));
}
}
Rezultat
bucăți: 10.0
prețuri: 19.75
amestec: [0.5, 1, 2, 3], sumă: 6.5
Greșeli frecvente
- Folosești tipuri brute:
List lista = new ArrayList();. Pierzi verificarea, iar-Xlintdă[rawtypes]și[unchecked]. Scrie mereu parametrul:List<String>. - Scrii
List<int>:unexpected type; required: reference, found: int. Folosește clasa wrapperInteger. - Crezi că
List<Integer>este unList<Number>. Nu este, deșiIntegeresteNumber; foloseșteList<? extends Number>ca parametru. - Încerci
new T()saunew T[10]: din cauza ștergerii tipului, la rulareTnu mai este cunoscut și codul nu compilează.
Comenzi înrudite
Expresii lambda: O lambda este o funcție scurtă, fără nume, scrisă ca parametri -> corp, folosită oriunde se așteaptă o interfață funcțională.Comparatori (Comparable și Comparator): Comparable dă ordinea naturală a unei clase (compareTo); Comparator descrie orice altă ordine și se combină cu comparing, thenComparing și reversed.Tipuri wrapper și autoboxing: Fiecare primitiv are o clasă „înveliș” (Integer, Double, Character…); conversia automată între ele se numește autoboxing.Interfețe (interface, implements): O interfață (interface) descrie ce metode trebuie să aibă o clasă; clasa o promite cu implements și poate implementa mai multe interfețe.List: Interfața pentru liste ordonate: elementele au poziții (0, 1, 2…), se pot repeta și se pot citi după index.Map: Interfața pentru dicționare: perechi cheie → valoare, în care găsești rapid valoarea după cheie. Cheile sunt unice.