Cuvântul cheie „final” în Java

1. Prezentare generală

În timp ce moștenirea ne permite să refolosim codul existent, uneori trebuie să stabilim limitări ale extensibilității din diverse motive; ultimul cuvânt cheie ne permite să facă exact acest lucru.

În acest tutorial, vom analiza ce înseamnă cuvântul cheie final pentru clase, metode și variabile.

2. Clasele finale

Clasele marcate ca finale nu pot fi prelungite. Dacă ne uităm la codul bibliotecilor de bază Java, vom găsi acolo multe clase finale . Un exemplu este clasa String .

Luați în considerare situația dacă putem extinde clasa String , suprascrie oricare dintre metodele sale și înlocuim toate instanțele String cu instanțele subclasei noastre specifice String .

Rezultatul operațiilor asupra obiectelor String va deveni imprevizibil. Și având în vedere că clasa String este folosită peste tot, este inacceptabilă. De aceea, clasa String este marcată ca finală .

Orice încercare de a moșteni dintr-o clasă finală va provoca o eroare a compilatorului. Pentru a demonstra acest lucru, să creăm clasa finală Cat :

public final class Cat { private int weight; // standard getter and setter }

Și să încercăm să o extindem:

public class BlackCat extends Cat { }

Vom vedea eroarea compilatorului:

The type BlackCat cannot subclass the final class Cat

Rețineți că finală cuvântul cheie într - o declarație de clasă nu înseamnă că obiectele din această clasă sunt imuabile . Putem schimba câmpurile obiectului Cat în mod liber:

Cat cat = new Cat(); cat.setWeight(1); assertEquals(1, cat.getWeight()); 

Pur și simplu nu o putem extinde.

Dacă respectăm cu strictețe regulile unui bun design, ar trebui să creăm și să documentăm cu atenție o clasă sau să o declarăm definitivă din motive de siguranță. Cu toate acestea, ar trebui să avem precauție atunci când creăm clase finale .

Observați că finalizarea unei clase înseamnă că niciun alt programator nu o poate îmbunătăți. Imaginați-vă că folosim o clasă și nu avem codul sursă pentru aceasta și există o problemă cu o metodă.

Dacă clasa este finală, nu o putem extinde pentru a suprascrie metoda și a remedia problema. Cu alte cuvinte, pierdem extensibilitatea, unul dintre beneficiile programării orientate pe obiecte.

3. Metode finale

Metodele marcate ca finale nu pot fi anulate. Când proiectăm o clasă și considerăm că o metodă nu ar trebui să fie ignorată, putem face această metodă definitivă . De asemenea, putem găsi multe metode finale în bibliotecile de bază Java.

Uneori nu este necesar să interzicem complet o extensie de clasă, ci doar să prevenim suprascrierea unor metode. Un bun exemplu în acest sens este clasa Thread . Este legal să îl extindeți și astfel să creați o clasă de fire personalizată. Dar metodele sale isAlive () sunt finale .

Această metodă verifică dacă un fir este viu. Este imposibil să înlocuiți corect metoda isAlive () din mai multe motive. Una dintre ele este că această metodă este nativă. Codul nativ este implementat într-un alt limbaj de programare și este adesea specific sistemului de operare și hardware-ului pe care rulează.

Să creăm o clasă Dog și să-i facem metoda sonoră () finală :

public class Dog { public final void sound() { // ... } }

Acum, să extindem clasa Dog și să încercăm să-i anulăm metoda sound () :

public class BlackDog extends Dog { public void sound() { } }

Vom vedea eroarea compilatorului:

- overrides com.baeldung.finalkeyword.Dog.sound - Cannot override the final method from Dog sound() method is final and can’t be overridden

Dacă unele metode din clasa noastră sunt apelate prin alte metode, ar trebui să luăm în considerare ca metodele numite să fie definitive . În caz contrar, suprascrierea acestora poate afecta munca apelanților și poate provoca rezultate surprinzătoare.

Dacă constructorul nostru apelează alte metode, ar trebui, în general, să declarăm aceste metode finale din motivul de mai sus.

Care este diferența dintre a face toate metodele finalei clasei și a marca definitivă clasa ? În primul caz, putem extinde clasa și adăuga noi metode.

În al doilea caz, nu putem face acest lucru.

4. Variabile finale

Variabilele marcate ca finale nu pot fi realocate. Odată ce o variabilă finală este inițializată, aceasta nu poate fi modificată.

4.1. Variabile primitive finale

Să declarăm o variabilă finală primitivă i, apoi să îi atribuim 1.

Și să încercăm să îi atribuim o valoare 2:

public void whenFinalVariableAssign_thenOnlyOnce() { final int i = 1; //... i=2; }

Compilatorul spune:

The final local variable i may already have been assigned

4.2. Variabile de referință finale

Dacă avem o variabilă de referință finală , nu o putem nici reatribui. Dar asta nu înseamnă că obiectul la care se referă este imuabil . Putem modifica proprietățile acestui obiect în mod liber.

Pentru a demonstra acest lucru, să declarăm variabila de referință finală cat și să o inițializăm:

final Cat cat = new Cat();

Dacă încercăm să îl reatribuim, vom vedea o eroare a compilatorului:

The final local variable cat cannot be assigned. It must be blank and not using a compound assignment

Dar putem schimba proprietățile instanței Cat :

cat.setWeight(5); assertEquals(5, cat.getWeight());

4.3. Câmpuri finale

Câmpurile finale pot fi constante sau câmpuri write-once. Pentru a le distinge, ar trebui să punem o întrebare - am include acest câmp dacă am serializa obiectul? Dacă nu, atunci nu face parte din obiect, ci este o constantă.

Rețineți că, conform convențiilor de denumire, constantele de clasă ar trebui să fie majuscule, cu componentele separate prin caractere de subliniere („_”):

static final int MAX_WIDTH = 999;

Rețineți că orice câmp final trebuie inițializat înainte de finalizarea constructorului .

Pentru câmpurile finale statice , aceasta înseamnă că le putem inițializa:

  • după declarație așa cum se arată în exemplul de mai sus
  • în blocul de inițializator static

De exemplu , câmpurile finale , aceasta înseamnă că le putem inițializa:

  • la declarare
  • în blocul inițializatorului de instanță
  • în constructor

În caz contrar, compilatorul ne va da o eroare.

4.4. Argumente finale

Cuvântul cheie final este, de asemenea, legal pentru a pune înaintea argumentelor metodei. Un argument final nu poate fi schimbat în interiorul unei metode :

public void methodWithFinalArguments(final int x) { x=1; }

Atribuirea de mai sus provoacă eroarea compilatorului:

The final local variable x cannot be assigned. It must be blank and not using a compound assignment

5. Concluzie

În acest articol, am aflat ce înseamnă cuvântul cheie final pentru clase, metode și variabile. Deși este posibil să nu folosim adesea cuvântul cheie final în codul nostru intern, poate fi o soluție bună de proiectare.

Ca întotdeauna, codul complet pentru acest articol poate fi găsit în proiectul GitHub.