Sunteți pe pagina 1din 10

În cadrul lecţiei ce urmează vor fi abordate noţiunile de moştenire şi

polimorfism în limbajul de programare Java, pe care le vom prezenta


cu ajutorul unor exemple practice.

Moştenirea

După cum am spus deja, moştenirea reprezintă caracteristica clasei de


a accepta caracteristicile unei alte clase, unde clasa care este
moştenită are statutul de clasă părinte, în timp ce clasa care
moşteneşte are statutul de copil al clasei părinte.

În timpul moştenirii există câteva reguli care trebuie respectate şi de


care depinde tratarea elementelor moştenite. Prima regulă este aceea
că clasa care moşteneşte poate avea doar o singură clasă părinte.
Apoi, nu toate atributele clasei părinte se transmit la clasa copil, ci
doar atributele public, protected şi package-private. Ne vom reaminti
de faptul că modificatorul public face ca atributul să fie vizibil în
interiorul şi în exteriorul clasei, în toate pachetele, în timp ce protected
face ca atributul să fie vizibil în interiorul clasei în care acesta este
creat, în interiorul clasei moştenitoare şi în interiorul claselor din
acelaşi pachet, însă şi în afara pachetului doar în clasele moştenite,
unde aceste atribute nu trebuie specificate explicit în clasa care
moştenitoare. Modificatorul de acces package-private este
modificatorul care nu posedă cuvânt cheie, respectiv specificarea lui se
realizează prin omiterea cuvântului cheie. Acesta indică faptul că un
element este vizibil din toate clasele aceluiaşi pachet.

Vom vedea acum moştenirea printr-un exemplu real. Vom crea o


ierarhie de moştenire între trei clase în Java.
Imaginea 17.1 Moştenirea

Pentru început creăm clasa Person.

public class Person {

public String firstName;


public String lastName;

public Person(String firstName, String lastName)


{
this.firstName = firstName;
this.lastName = lastName;
}

public void show() {

System.out.println("Person: " + firstName + " " + lastName);

}
}

În clasa Person sunt declarate două câmpuri, dintre care unul


reprezintă prenumele, iar celălalt reprezintă numele persoanei.

Pe lângă aceste câmpuri, este definit şi constructorul care se foloseşte


pentru introducerea valorilor câmpului în timpul creării obiectelor.

Clasa Person conţine şi o metodă, este vorba de metoda show, care se


foloseşte pentru reprezentarea datelor despre persoană.

După aceea, putem crea clasa Student care moşteneşte clasa Person.

public class Student extends Person {


public String indexNumber;

public Student(String firstName, String lastName, String indexNumber)


{
super(firstName, lastName);
this.indexNumber = indexNumber;
}

În exemplul de mai sus, clasa Student moşteneşte clasa Person şi


atributele sale firstName şi lastName. Moştenirea este realizată prin
folosirea cuvântului cheie extends. De asemenea, clasa Student
conţine şi un atribut suplimentar, este vorba de indexNumber. Este
foarte important de menţionat că clasele moştenesc membri (câmpuri,
metode), însă nu şi constructori. Fiindcă în clasa Person constructorul
este definit explicit (adică nu este folosit cel implicit), constructorul
trebuie definit şi în cadrul clasei Student. Ca parametri ai
constructorului sunt specificate valorile care vor fi atribuite câmpurilor
moştenite din clasa părinte. În cadrul corpului constructorului, folosind
cuvântul cheie super, este executat apelul constructorului de la clasa
părinte şi în el sunt transmişi parametrii pentru atribuirea valorilor la
câmpurile pentru prenume şi nume care sunt moştenite.

Acum, obiectul de tip Student se creează în felul următor:


Student student = new Student("John", "Smith", "10/2014");

Dacă specificăm denumirea obiectului şi după aceea introducem


punctul, înseamnă că acest obiect va conţine şi elementele pe care le-
a moştenit din clasa părinte.

Imaginea 17.2 Elemente moştenite de la clasa părinte

În imaginea de mai sus putem vedea că obiectul de tip Student conţine


atributele care sunt declarate în clasa Person. Pe lângă aceste atribute,
obiectul de tip Student mai conţine şi metoda show care este definită
în cadrul clasei părinte.

Uneori vom dori ca în clasa părinte să introducem o anumită valoare


care nu ar trebui să se modifice. De exemplu, clasa Person reprezintă
toate persoanele dintr-o instituţie educaţională, aşadar, în acest caz,
am fi putut să adăugăm un câmp în cadrul acestei clase:

public final String SCHOOL_NAME = "UTM";


Şi clasa Student, dar şi clasa Professor (pe care o vom defini mai
târziu), care moştenesc clasa Person, vor vrea câmpul SCHOOL_NAME.
În clasa Professor, acest câmp se referă la instituţia în cadrul căreia
este angajat, iar în clasa Student se referă la instituţia în cadrul căreia
studiază.

Acest câmp este declarat prin folosirea cuvântului cheie final, lucru
prin care este specificat că nu se va putea modifica.

Dacă acum încercăm să modificăm câmpul, editorul va semnala o


eroare:

Imaginea 17.3 Încercare de modificare a câmpului sigilat

Modificatorul final se poate atribui şi clasei propriu-zise. Atunci,


moştenirea ei nu va mai fi posibilă. Dacă am fi modificat declararea
clasei Person în felul următor:

public final class Person {}

programul ar fi semnalat din nou o eroare, deoarece clasa Student nu


ar fi putut să moştenească această clasă din cauza modificatorului
final. Modificatorul final nu influenţează doar instanţierea clasei,
aşadar, următorul cod va funcţiona fără probleme.

Person p = new Person("John", "Smith");


Polimorfismul

În programarea orientată pe obiecte, noţiunea de polimorfism se


reflectă prin două noţiuni, adică tehnici. Este vorba de overriding
(suprascriere) şi overloading (supraîncărcare).

Overriding înseamnă că fiecare clasă care moşteneşte o altă clasă, iar


în afară de aceasta şi o metodă, poate defini propria implementare a
metodei moştenite. În situaţia noastră, avem deja clasa părinte cu
denumirea de Person şi două clase care o moştenesc: Professor şi
Student. Am creat deja clasa Student, aşadar ne mai rămâne să creăm
clasa Professor.

public class Professor extends Person {

public String className;

public Professor(String firstName, String lastName, String className) {

super(firstName, lastName);
this.className = className;
}
}

Am creat clasa Professor în aşa fel încât să moştenească clasa Person


şi să conţină denumirea unui obiect de studiu pe care îl predă
profesorul. Constructorul este definit la fel ca şi constructorul clasei
Student. Vom defini acum şi metoda show în cadrul clasei Professor:

public void show() {


System.out.println("Professor " + firstName + " " + lastName + " teaches: " + className);

Dacă definim această metodă în cadrul mediului de dezvoltare


NetBeans, editorul ne va recomanda adăugarea semnului @Override.
Acest semn nu are nicio semnificaţie când este vorba de executarea
codului sau de sintaxă, ci se foloseşte ca o adnotare a faptului că
metoda din clasa părinte este suprascrisă de metoda din clasa
moştenitorului.

Imaginea 17.4 Adăugarea adnotării override

Metoda show se poate reimplementa şi în cadrul clasei Student:

public void show()


{
System.out.println("Student: " + firstName + " " + lastName + " ID No. " + indexNumber);
}

În acest mod, noi avem o metodă cu aceeaşi semnătură, declarată în


toate cele trei clase: în clasa părinte Person şi în cele două clase
moştenitoare, Student şi Professor. Vom apela acum toate aceste
metode şi vom vedea ce vom obţine la ieşire.
public static void main(String[] args) {

Person person = new Person("John", "Davidson");


Student student = new Student("John", "Smith", "10/2014");
Professor professor = new Professor("Edward", "Owen", "Java Programming");

person.show();
student.show();
professor.show();
}

La ieşire obţinem rezultatul fiecărei metode în parte:

Person: John Davidson


Student: John Smith ID No. 10/2014
Professor Edward Owen teaches: Java Programming

Aceasta ne indică faptul că avem un caz de polimorfism.

Ne mai rămâne să ne familiarizăm cu noţiunea de overloading. Acesta


se manifestă prin faptul că în cadrul unei clase pot exista mai multe
definiţii pentru un comportament identic. Aceasta înseamnă că într-o
clasă pot exista mai multe metode cu aceeaşi denumire, însă cu diferiţi
parametri de intrare. În timpul apelului metodei, în funcţie de
parametrii transmişi, va fi apelată metoda corespunzătoare.

Ne vom imagina acum un exemplu de clasă pentru executarea unor


operaţii de calcul. În cadrul acestei clase putem avea câteva metode
add, aceasta în funcţie de numărul de parametri pe care îi acceptă
metoda.

class Calc
{
public static double add(double a, double b)
{
return a+b;
}

public static double add(double a, double b, double c)


{
return a+b+c;
}
}

Dacă acum apelăm aceste metode:

public static void main(String[] args) {

double res1 = Calc.add(4, 6);


double res2 = Calc.add(4, 6, 8);

System.out.println(res1);
System.out.println(res2);
}

la ieşire vom obţine rezultatele calculului:

10.0
18.0

Overloading există doar la metodele care au aceeaşi denumire şi


parametrii diferiţi de intrare. Când spunem parametri de intrare diferiţi,
ne referim fie la un număr diferit de parametri, fie la tipuri diferite de
parametri de intrare. Cazul în care două metode cu aceeaşi denumire
au acelaşi număr de parametri de acelaşi tip, dar cu denumiri diferite
ale parametrilor, nu este overloading.
public static double add(double a, double b)
{
return a+b;
}

public static double add(double c, double d)


{
return a+b;
}

Exemplul celor două metode de mai sus cu siguranţă nu este


overloading. Mai mult, exemplul de mai sus nu este nici corect din
punct de vedere sintactic, fiindcă nu se pot defini două metode cu
aceeaşi denumire, cu acelaşi număr de parametri şi de acelaşi tip
(adică două metode cu aceeași semnătură). Mediul de dezvoltare vă va
indica clar că este vorba de o eroare (metodă dublă).

S-ar putea să vă placă și