MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Moștenire

Unele clase pot moșteni de la altele. Moștenirea permite reducerea volumului de cod în clasele derivate. De exemplu, să luăm următoarele clase:

class Person{
   name;
   age;
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
}
class Employee{
   name;
   age;
   company;
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
   work(){
       console.log(`${this.name} works in ${this.company}`);
   }
}
 
const tom = new Person();
tom.name = "Tom";
tom.age= 34;
const bob = new Employee();
bob.name = "Bob";
bob.age = 36;
bob.company = "Google";
tom.print();    // Name: Tom  Age: 34
bob.print();    // Name: Bob  Age: 36
bob.work();     // Bob works in Google

Aici sunt definite două clase - Person, care reprezintă o persoană, și Employee, care reprezintă un angajat al unei întreprinderi. Ambele clase funcționează bine, putem crea obiecte pentru ele, dar observăm că clasa Employee repetă funcționalitatea clasei Person, deoarece un angajat este, de asemenea, o persoană, pentru care se pot defini proprietățile name și age și metoda print.

Moștenirea permite ca unele clase să obțină automat funcționalitatea altor clase, astfel reducând volumul de cod. Pentru a realiza moștenirea unei clase de la alta, se utilizează cuvântul cheie extends:

class Base{}
class Derived extends Base{}

După numele clasei-derivat se adaugă cuvântul cheie extends, urmat de numele clasei de la care dorim să moștenim funcționalitatea.

Astfel, să modificăm clasele Person și Employee, aplicând moștenirea:

class Person{
   name;
   age;
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
}
class Employee extends Person{
   company;
   work(){
       console.log(`${this.name} works in ${this.company}`);
   }
}
 
const tom = new Person();
tom.name = "Tom";
tom.age= 34;
const bob = new Employee();
bob.name = "Bob";
bob.age = 36;
bob.company = "Google";
tom.print();    // Name: Tom  Age: 34
bob.print();    // Name: Bob  Age: 36
bob.work();     // Bob works in Google

Acum, clasa Employee moștenește de la clasa Person. În acest context, clasa Person mai este numită și clasă de bază sau părinte, în timp ce clasa Employee este clasă derivată sau clasă-copil. Deoarece clasa Employee moștenește funcționalitatea de la Person, nu este nevoie să redefinim proprietățile name, age și metoda print în clasa Employee. Astfel, codul clasei Employee a devenit mai scurt, dar rezultatul programului rămâne același.

Moștenirea unei clase cu constructor

Împreună cu întregul funcțional, clasa derivată moștenește și constructorul clasei de bază. De exemplu, să definim un constructor în clasa de bază Person:

class Person{
   constructor(name, age){
       this.name = name;
       this.age = age;
   }
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
}
class Employee extends Person{
   company;
   work(){
       console.log(`${this.name} works in ${this.company}`);
   }
}
 
const tom = new Person("Tom", 34);
tom.print();    // Name: Tom  Age: 34

const sam = new Employee("Sam", 25);    // constructor moștenit
sam.print();    // Name: Sam  Age: 25

În acest caz, clasa Person definește un constructor cu doi parametri. În acest scenariu, clasa Employee moștenește acest constructor și îl utilizează pentru a crea un obiect Employee.

Definirea unui constructor într-o clasă derivată și cuvântul cheie super

O clasă derivată poate, de asemenea, să-și definească propriul constructor. Dacă o clasă derivată definește un constructor, acesta trebuie să apeleze constructorul clasei de bază. Pentru a accesa funcționalitatea clasei de bază, inclusiv pentru a apela constructorul clasei de bază, în clasa derivată se folosește cuvântul cheie super.

class Person{
   constructor(name, age){
       this.name = name;
       this.age = age;
   }
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
}
class Employee extends Person{
   
   constructor(name, age, company){
       super(name, age);
       this.company = company;
   }
   work(){
       console.log(`${this.name} works in ${this.company}`);
   }
}
 
const tom = new Person("Tom", 34);
tom.print();    // Name: Tom  Age: 34

const sam = new Employee("Sam", 25, "Google");
sam.print();    // Name: Sam  Age: 25
sam.work();     // Sam works in Google

Clasa Employee definește propriul constructor cu trei parametri, iar prima linie din acesta face referire la constructorul clasei de bază Person:

super(name, age);

Deoarece constructorul clasei Person are doi parametri, trebuie să-i fie furnizate două valori. În plus, constructorul clasei de bază trebuie să fie apelat înainte de a face referire la proprietățile obiectului curent prin intermediul cuvântului cheie this.

Suprascrierea metodelor clasei de bază

O clasă derivată, la fel ca și în cazul constructorului, poate să suprascrie metodele clasei de bază. În exemplul de mai sus, metoda print() a clasei Person afișează numele și vârsta persoanei. Dar ce facem dacă vrem ca, pentru un angajat, metoda print() să afișeze și compania la care lucrează? În acest caz, putem defini în clasa Employee propria sa metodă print():

class Person{
   constructor(name, age){
       this.name = name;
       this.age = age;
   }
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
}
class Employee extends Person{
   
   constructor(name, age, company){
       super(name, age);
       this.company = company;
   }
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
       console.log(`Company: ${this.company}`);
   }
}
const sam = new Employee("Sam", 25, "Google");
sam.print();    // Name: Sam  Age: 25
               // Company: Google

Cu toate acestea, în codul de mai sus observăm că prima linie a metodei print() în clasa Employee practic repetă codul metodei print() din clasa Person. În acest caz, este doar o singură linie, dar în alte situații, codul repetitiv ar putea fi mai extins. Pentru a evita repetarea, putem, din nou, să accesăm implementarea metodei print() din clasa părinte Person utilizând super:

class Person{
   constructor(name, age){
       this.name = name;
       this.age = age;
   }
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
}
class Employee extends Person{
   
   constructor(name, age, company){
       super(name, age);
       this.company = company;
   }
   print(){
       super.print();
       console.log(`Company: ${this.company}`);
   }
}
const sam = new Employee("Sam", 25, "Google");
sam.print();    // Name: Sam  Age: 25
               // Company: Google

Deci, în acest caz, apelul:

super.print();

Reprezintă o apelare a implementării metodei din clasa de bază. Astfel, utilizând this și super, putem distinge între accesarea funcționalității clasei curente sau a clasei de bază.

Moștenirea și câmpurile și metodele private

La moștenire, trebuie să avem în vedere că clasa derivată poate accesa orice funcționalitate a clasei de bază, cu excepția câmpurilor și metodelor private. De exemplu:

class Person{
   #name;
   constructor(name, age){
       this.#name = name;
       this.age = age;
   }
   print(){
       console.log(`Name: ${this.#name}  Age: ${this.age}`);
   }
}
class Employee extends Person{
   
   constructor(name, age, company){
       super(name, age);
       this.company = company;
   }
   print(){
       super.print();
       console.log(`Company: ${this.company}`);
   }
   work(){
       console.log(`${this.#name} works in ${this.company}`);  //! Eroare - câmpul #name nu este accesibil din clasa Employee
   }
}

În acest caz, câmpul #name în clasa Person este definit ca privat, astfel că este accesibil doar în interiorul acelei clase. Încercarea de a accesa acest câmp în clasa derivată Employee va duce la o eroare, indiferent dacă se face referire prin this.#name sau super.#name. Dacă este necesar, în clasa de bază se pot defini getteri și setteri care să acceseze câmpurile private. Apoi, în clasele derivate, se poate accesa câmpurile private ale clasei de bază prin intermediul acestor getteri și setteri.

Verificarea apartenenței unui obiect la o clasă

Faptul că o clasă derivată este moștenită de la o anumită clasă de bază indică faptul că obiectul clasei derivate este, de asemenea, un obiect al clasei de bază. Se poate verifica clasa obiectului utilizând operatorul instanceof:

class Person{
   constructor(name, age){
       this.name = name;
       this.age = age;
   }
   print(){
       console.log(`Name: ${this.name}  Age: ${this.age}`);
   }
}
class Employee extends Person{
   
   constructor(name, age, company){
       super(name, age);
       this.company = company;
   }
   print(){
       super.print();
       console.log(`Works in ${this.company}`);
   }
}
class Manager extends Person{

   constructor(name, age, company){
       super(name, age);
       this.company = company;
   }
   print(){
       super.print();
       console.log(`Manager in ${this.company}`);
   }
}
const sam = new Employee("Sam", 25, "Google");
console.log(sam instanceof Person); // true
console.log(sam instanceof Employee); // true
console.log(sam instanceof Manager); // false

În acest caz, constanta `sam` reprezintă un obiect al clasei Employee, care este moștenită de la Person. Prin urmare, expresiile `sam instanceof Person` și `sam instanceof Employee` vor returna true. Cu toate acestea, obiectul nu este un obiect al clasei Manager, așa că expresia `sam instanceof Manager` va returna false.

← Lecția anterioară Lecția următoare →