Overriding / Suprascriere

Suprascrierea este mecanismul de rescriere a unei metode într-o clasă derivată. Acest lucru ne permite să definim o implementare mai specializată pentru clasa respectivă.

Beneficiile suprascrierii includ: abilitatea de a defini un comportament specific subclasei, ceea ce înseamnă că subclasa poate implementa metodele super-clasei în funcţie de cerinţele specifice ale acesteia. În termeni OOP, suprascrierea înseamna să redefinim funcţionalitatea unei metode existente.

class Animal {
    public void move() {
        System.out.println("Animals can move");
    }
}

class Dog extends Animal {
    public void move(){
        System.out.println("Dogs can walk and run");
    }
    
    public void bark(){
        System.out.println("Dogs can bark");
    }
}

public class TestDog {
    public static void main(String args[]) {
        Animal a = new Animal(); // Animal reference and object
        Animal b = new Dog(); // Animal reference but Dog object
        a.move(); // runs the method in Animal class
        b.move(); //runs the method in Dog class
    }
}

Codul de mai sus va produce următorul rezultat:

Animals can move
Dogs can walk and run

În exemplul de mai sus, putem observa că obiectul b, chiar dacă este de tipul Animal apelează metoda din clasa Dog. Motivul este următorul: la compilare, verificarea este făcută pe tipul referinţei. Cu toate acestea, la rulare, JVM rulează metoda care aparţine tipului instanţiat. Aşadar, în exemplul de mai sus, programul se va compila corect, clasa Animal având metoda move(). Apoi, la rulare se va executa metoda specifică clasei Dog.

Mai este un amănunt de care trebuie să ţinem seama. Avem următorul exemplu:

public class TestDog {
    public static void main(String args[]){
        Animal a = new Animal(); // Animal reference and object
        Animal b = new Dog(); // Animal reference but Dog object
        a.move(); // runs the method in Animal class
        b.move(); //runs the method in Dog class
        b.bark();
    }
}

Codul de mai sus va produce următorul rezultat:

TestDog.java:30: cannot find symbol
symbol : method bark()
location:class Animal
b.bark();
 ^

Acest program va arunca o eroare de compilare, deoarece variabila b este de tipul Animal, care nu are nicio metodă bark().

Reguli pentru suprascrierea metodelor

  • Lista argumentelor ar trebui să fie exact la fel ca şi lista din metoda suprascrisă.
  • Tipul returnat ar trebui să fie la fel sau un subtip a tipului declarat în metoda originală din superclasă.
  • Nivelul de acces nu poate fi mai restrictiv decât nivelul de acces al metodei suprascrise. De exemplu, dacă metodele din superclasă sunt declarate public, atunci metoda din subclasă nu poate fi declarată nici private nici protected.
  • Metodele de instanţă pot fi suprascrise doar dacă sunt moştenite de subclasă.
  • O metodă declarată final nu poate fi suprascrisă.
  • O metodă declarată static nu poate fi suprascrisă, dar poate fi re-declarată.
  • Dacă o metodă nu poate fi moştenită, atunci nu poate fi suprascrisă.
  • O subclasă din acelaşi pachet ca şi clasa de bază poate suprascrie orice metodă care nu este declarată private sau final.
  • O subclasă dintr-un pachet diferit poate declara doar metodele non-final care sunt declarate public sau protected.
  • Constructorii nu pot fi suprascrişi.

Assignment

Realizaţi următoarea ierarhie de clase: porniţi de o clasă Om, care are 3 atribute (nume, vârstă, sex), care va fi extinsă de 2 clase (Cursant şi Angajat), care să aibă fiecare un atribut propriu. Acestea vor fi extinse la rândul lor de alte clase, fiecare cu câte un atribut propriu: Cursant de Elev şi Student, iar Angajat de Programator, Contabil, Trainer. Afişaţi-le atributele. Folosiţi şi metoda toString().

Om
String nume
int varsta
char sex
Cursant Angajat
String institutie int salariu
Elev Student Programator Contabil Trainer
String clasaint anStudiu String limbajboolean isSeniorint nrCursanti

Solutie

Solutie

public class Om {

   public String nume;
   public int varsta;
   public char sex;
   

   public Om(String nume, int varsta, char sex) {
      super();
      this.nume = nume;
      this.varsta = varsta;
      this.sex = sex;
   }

   @Override
   public String toString() {
      return "\nOm [nume=" + nume + ", varsta=" + varsta + ", sex=" + sex + "]";
   }

}
Subclasa Cursant şi copiii ei:
public class Cursant extends Om {

   public String institutie;

   public Cursant(String nume, int varsta, char sex, String institutie) {
      
      super(nume, varsta, sex);
      this.institutie = institutie;
   }

}
public class Elev extends Cursant {

   public String clasa;

   public Elev(String nume, int varsta, char sex, String institutie, String clasa) {
   
      super(nume, varsta, sex, institutie);
      this.clasa = clasa;
   }

   @Override
   public String toString() {
      return "\nElev [clasa=" + clasa + ", institutie=" + institutie + ", nume=" + nume + ", varsta=" + varsta
            + ", sex=" + sex + "]";
   }
   
}
public class Student extends Cursant {

   public int anStudiu;

   public Student(String nume, int varsta, char sex, String institutie, int anStudiu) {
      super(nume, varsta, sex, institutie);
      this.anStudiu = anStudiu;
   }

   @Override
   public String toString() {
      return "\nStudent [anStudiu=" + anStudiu + ", institutie=" + institutie + ", nume=" + nume + ", varsta=" + varsta
            + ", sex=" + sex + "]";
   }
   
}
Subclasa Angajat şi copiii ei:
public class Angajat extends Om {

   public int salariu;

   public Angajat(String nume, int varsta, char sex, int salariu) {
      
      super(nume, varsta, sex);
      this.salariu = salariu;

   }

}
public class Programator extends Angajat{

   public String limbaj;

   public Programator(String nume, int varsta, char sex, int salariu, String limbaj) {
      
      super(nume, varsta, sex, salariu);
      this.limbaj = limbaj;
   }

   @Override
   public String toString() {
      return "\nProgramator [limbaj=" + limbaj + ", salariu=" + salariu + ", nume=" + nume + ", varsta=" + varsta
            + ", sex=" + sex + "]";
   }

}
public class Contabil extends Angajat {

   public boolean isSenior;

   public Contabil(String nume, int varsta, char sex, int salariu, boolean isSenior) {
      super(nume, varsta, sex, salariu);
      this.isSenior = isSenior;
   }

   @Override
   public String toString() {
      return "\nContabil [isSenior=" + isSenior + ", salariu=" + salariu + ", nume=" + nume + ", varsta=" + varsta
            + ", sex=" + sex + "]";
   }
}
public class Trainer extends Angajat {

   public int nrCursanti;

   public Trainer(String nume, int varsta, char sex, int salariu, int nrCursanti) {
      super(nume, varsta, sex, salariu);
      this.nrCursanti = nrCursanti;
   }

   @Override
   public String toString() {
      return "\nTrainer [nrCursanti=" + nrCursanti + ", salariu=" + salariu + ", nume=" + nume + ", varsta=" + varsta
            + ", sex=" + sex + "]";
   }

}
Clasa de test:
public class Principala {

   public static void main(String[] args) {
      
      // cream obiectele
      Om om1 = new Om("Adam", 45, 'M');
      Om om2 = new Om("Eva", 44, 'F');

      Angajat angajat1 = new Angajat("Dan", 24, 'M', 4000);
      Programator programator1 = new Programator("Diana", 32, 'F', 2000, "Python");
      Programator programator2 = new Programator("Tom", 42, 'M', 3000, "Java");
      Trainer trainer1 = new Trainer("Gilles", 28, 'M', 1500, 15);
      Contabil contabil1 = new Contabil("Jeny", 34, 'F', 2000, true);
      Cursant cursant1 = new Cursant("John", 20, 'M', "UPB");

      Elev elev1 = new Elev("Bill", 16, 'M', "Caragiale", "11B");
      Elev elev2 = new Elev("Phil", 14, 'M', "Sava", "10A");
      Student student1 = new Student("Lidia", 19, 'F', "ASE", 1);

      // adaugare in ArrayList
      ArrayList<Om> oameni = new ArrayList<Om>();
      
      /* ArrayList-ul de tip Om poate primi si 
      obiecte din toate clasele care o extind */
      
      oameni.add(programator1);
      oameni.add(programator2);
      oameni.add(trainer1);
      oameni.add(contabil1);
      oameni.add(elev1);
      oameni.add(elev2);
      oameni.add(student1);

      // afisare directa a ArrayList-ului; aceasta va folosi Stringul returnat de ArrayList
      System.out.println(oameni);
      
      // afisarea directa a atributelor cu iterare
      for (Om om : oameni) {
         
         /* doarece ArrayList-ul contine obiecte din mai multe subclase
          trebuie intai sa facem downcast la clasa potrivita, inainte de 
          a putea accesa un atribut (nu toti oamenii sunt programatori 
          si nu toti stiu limbaje de programare) */
         
         // varianta 1 - metoda getClass()
         if(om.getClass() == Programator.class){
            Programator temp = (Programator)om;
            System.out.println(  temp.limbaj);
         }
         
         // varianta 2 - cuvantul cheie instanceof
         if( om  instanceof Programator){
            Programator temp = (Programator)om;
            System.out.println(temp.limbaj);
         }
         
      }
      
   }
}

You could leave a comment if you were logged in.