Ce face final (și static final)
Pe o variabilă (locală, parametru sau câmp), final înseamnă că poate primi valoare o singură dată. Un câmp final trebuie inițializat la declarare sau în fiecare constructor. Atenție: final blochează referința, nu obiectul: într-o listă final poți adăuga elemente, dar nu poți face variabila să arate spre altă listă.
Combinația static final creează o constantă de clasă, scrisă după convenție cu majuscule și liniuță jos: static final double TVA = 0.21;. Există o singură copie, care nu se schimbă niciodată.
Pe o metodă, final interzice rescrierea în subclase. Pe o clasă, interzice extinderea: String, Integer și Math sunt final. Variabilele locale folosite într-o lambda sau într-o clasă anonimă trebuie să fie final sau „efectiv final” (să nu fie modificate după inițializare).
Exemple
Exemplul 1: Constante static final și câmpuri final
TVA și MONEDA sunt constante comune. cod și pret sunt final, setate o singură dată în constructor; produsul este imutabil.
class Produs {
static final double TVA = 0.21;
static final String MONEDA = "lei";
final String cod;
final double pret;
Produs(String cod, double pret) {
this.cod = cod;
this.pret = pret;
}
double pretCuTva() {
return pret * (1 + TVA);
}
}
public class Main {
public static void main(String[] args) {
Produs p = new Produs("RO-123", 200);
System.out.printf("%s: %.2f %s fără TVA, %.2f %s cu TVA%n",
p.cod, p.pret, Produs.MONEDA, p.pretCuTva(), Produs.MONEDA);
// p.pret = 150; // eroare: cannot assign a value to final variable pret
// Produs.TVA = 0.19; // eroare: cannot assign a value to final variable TVA
}
}
Rezultat
RO-123: 200.00 lei fără TVA, 242.00 lei cu TVA
Exemplul 2: final blochează referința, nu obiectul
Lista orase este final, dar conținutul ei se poate schimba. Pentru o listă care nu se poate modifica deloc folosește List.of (imutabilă); modificarea ei aruncă excepție.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
final List<String> orase = new ArrayList<>();
orase.add("Iași");
orase.add("Vaslui");
System.out.println(orase);
// orase = new ArrayList<>(); // eroare: cannot assign a value to final variable orase
final List<String> fixe = List.of("Iași", "Vaslui");
try {
fixe.add("Bacău");
} catch (UnsupportedOperationException e) {
System.out.println("List.of nu se poate modifica: " + e.getClass().getSimpleName());
}
}
}
Rezultat
[Iași, Vaslui]
List.of nu se poate modifica: UnsupportedOperationException
Exemplul 3: Metodă finală și variabilă efectiv finală
calculeaza este final, deci subclasele nu o pot schimba. Variabila prag nu e declarată final, dar nu se mai modifică, deci este „efectiv finală” și poate fi folosită în lambda.
import java.util.List;
class Nota {
final double valoare;
Nota(double valoare) { this.valoare = valoare; }
final String calculeaza() {
return valoare >= 5 ? "promovat" : "nepromovat";
}
}
public class Main {
public static void main(String[] args) {
double prag = 9.0; // efectiv finală
List<Double> note = List.of(9.5, 7.0, 10.0, 4.5);
long excelente = note.stream().filter(n -> n >= prag).count();
System.out.println("note de cel puțin " + prag + ": " + excelente);
for (double n : note) {
System.out.println(n + " -> " + new Nota(n).calculeaza());
}
}
}
Rezultat
note de cel puțin 9.0: 2
9.5 -> promovat
7.0 -> promovat
10.0 -> promovat
4.5 -> nepromovat
Greșeli frecvente
- Crezi că
final Listface lista nemodificabilă. Doar variabila nu se poate reatribui; pentru conținut fix foloseșteList.ofsauList.copyOf. - Lași un câmp
finalneinițializat pe o ramură a constructorului:variable pret might not have been initialized. - Modifici o variabilă locală folosită într-o lambda:
local variables referenced from a lambda expression must be final or effectively final. - Încerci să extinzi o clasă
final, de exempluclass TextulMeu extends String:cannot inherit from final String.
Comenzi înrudite
static: static leagă un câmp sau o metodă de clasă, nu de un obiect: există o singură copie, accesată prin numele clasei.var: Declară o variabilă locală al cărei tip este dedus de compilator din valoarea inițială; tipul rămâne fix.Moștenire (extends): Cu extends o clasă moștenește câmpurile și metodele altei clase și le poate extinde sau rescrie; Java permite o singură superclasă.sealed, permits și non-sealed: O clasă sau interfață sealed își alege explicit subtipurile (permits); fiecare subtip este final, sealed sau non-sealed.record: record declară o clasă imutabilă pentru date: constructorul, accesorii, equals, hashCode și toString sunt generați automat.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ă.