Polimorfismul reprezintă abilitatea unei clase să se comporte ca o altă clasă de pe lanțul de moștenire, și de aceea conceptul de suprascriere a metodelor este foarte strâns legat.
Mai precis, polimorfismul permite obiectelor de tipuri diferite să fie tratate folosind o interfață comună sau o clasă de bază. Există două tipuri principale de polimorfism în POO: polimorfismul prin suprascrierea metodelor (override) și polimorfismul prin supraincarcarea metodelor (overloading).
Suprascrierea se referă la redefinirea metodelor existente în clasa părinte de către clasa copil în vederea specializării acestora.
În cazul suprascrierii se determină ce metodă va fi apelată, în mod dinamic, la runtime. Explicația este că decizia se face pe baza tipului obiectului care apelează metoda, deci a instanței, care e cunoscută la runtime. Din acest motiv, suprascrierea este cunoscută și ca polimorfism dinamic (Runtime polymorphism).
Câteva reguli pentru suprascriere sunt:
static
și final
nu pot fi suprascriseÎn exemplul de mai jos, metodele purr și getFeatures au fost suprascrise de tipul GrumpyCat.
class CatFeatures { } class GrumpyCatFeatures extends CatFeatures { } class GrumpyFeatures { } class Cat { public void purr() { System.out.println("purrrr"); } public CatFeatures getFeatures() { System.out.println("Cat getFeatures"); return new CatFeatures(); } public final void die() { System.out.println("Dying! frown emoticon"); } } class GrumpyCat extends Cat { @Override public void purr() { System.out.println("NO!"); } @Override public GrumpyCatFeatures getFeatures() { System.out.println("Grumpy getFeatures"); return new GrumpyCatFeatures(); } // compiler would complain if you included @Override here //@Override //public void die() { } // Cannot override the final method from Cat public static void main(String [] args) { ArrayList<Cat> cats = new ArrayList<Cat>(); cats.add(new Cat()); cats.add(new GrumpyCat()); for (Cat c : cats) { c.purr(); c.die(); c.getFeatures(); } } }
@Override
este complet opțională. Totuși este indicat să o includeți mereu când suprascrieți o metodă.
Motivele sunt simple:
public void doSmth(int x)
nu poate fi suprascrisă cu public void doSmth(Integer x)
Metoda cu argument de tip wrapper poate primi si null, insă cea cu tipul primitiv nu, de aceea, neputând să fie păstrată echivalența, nu este permisă aceasta suprascriere
Supraîncarcarea se referă la posibilitatea de a avea într-o clasă mai multe metode cu același nume, dar implementari diferite. În Java, compilatorul poate distinge între metode pe baza semnăturii lor, acesta fiind mecanismul din spatele supraîncărcarii.
Opțional, pe lângă semnătura metodei poate fi menționat și tipul excepțiilor ce pot fi aruncate din codul acesteia.
public class TRex { public void eat(Triceratops victim) { System.out.println("Take 5 huge bites"); } public boolean eat(Triceratops victim) { boolean satisfaction = false; if (victim.isJuicy()) { System.out.println("Eat and be satisfied"); satisfaction = true; } return satisfaction; } // Error "Duplicate method eat(Triceratops)" in type TRex
Observăm de asemenea că la compilare nu se ține cont de numele dat parametrilor. Astfel modificarea acestuia din victim în dino, spre exemplu, nu constituie o supraîncărcare validă.
Spre deosebire de suprascriere, supraîncărcarea are loc la compilare, motiv pentru care mai este numită și polimorfism static (compile time polymorphism). În aceasta fază compilatorul decide ce metodă este apelată pe baza tipului referinței și prin analiza numelui și a listei de parametri. La runtime, când este întalnit apelul unei metode supraîncărcate, deja se știe unde este codul care trebuie executat.
Mai jos avem un exemplu valid de supraîncărcare pentru metoda eat:
public class TRex { public void eat(Triceratops victim) { System.out.println("Take 5 huge bites"); } public void eat(Dromaeosaurus victim) { // parametru cu tip diferit System.out.println("Take 1 single bite"); } public void eat(Human firstCourse, Human secondCourse) { // numar si tipuri diferite de parametrii System.out.println("No humans to eat at the time"); } public int eat(Grass desert) { // parametru cu tip diferit, return type este irelevant System.out.println("Rather starve"); return 0; } public static void main(String [] args) { TRex john = new TRex(); john.eat(new Triceratops()); // "Take 5 huge bites" john.eat(new Dromaeosaurus()); // "Take 1 single bite" john.eat(new Human("Ana"), new Human("Andrei")); // "No humans to eat at the time" john.eat(new Grass()); // "Rather starve" } }
Task 1 [1p]
In clasa Employee adaugati 3 parametri `private`: name (String), salary (double) si age (int). Adaugati constructor cu toti cei 3 parametri si getteri si setteri pentru fiecare parametru.
In fiecare clasa-copil (`Manager`, `Developer`) adaugati un constructor care sa apeleze constructorul din clasa de baza (`super`).
In toate cele 3 clase adaugati metoda `toString` astfel:
- pentru `Employee` va afisa: Name: `name`, Salary: `salary`, Age: `age` - pentru `Manager` va afisa: Name: `name`, Salary: `salary`, Age: `age` [Position: Manager] - pentru `Developer` va afisa: Name: `name`, Salary: `salary`, Age: `age` [Position: Developer]
Task 2 [1p]
Implementati metoda `calculateBonus` in clasa `Employee` care va salariul * 0.05.
Suprascrieti metoda in cele doua clase copil astfel: - pentru `Manager` bonusul va fi salariul * 0.2 - pentru `Developer` bonusul va fi salariul * 0.15
Task 3 [1p]
Implementati metoda `validateEmployee` in clasa `Employee` care va verifica daca salariul este mai mare decat 0 si varsta este intre 18. Metoda va returna `Salary cannot be negative.` cand salariul este mai mic de 0 si `Employee age must be 18 or older.` cand varsta este mai mica de 18.
Task 4 [2p]
Pentru clasele `Manager` si `Developer` adaugati un parametru `private` care sa reprezinte numarul de angajati pe care il are fiecare manager, respectiv numarul de proiecte pe care le are fiecare developer. Adaugati getteri si setteri pentru acesti parametri. Adaugati un constructor care sa primeasca si acesti parametri si sa apeleze constructorul din clasa de baza.
In clasa `Employee` adaugati o metoda showDetails care sa afiseze mesajul `Employee details: `.
In clasele copil suprascrieti metoda astfel: - pentru `Manager` va afisa: `Employee details: Manager of X employees.` - pentru `Developer` va afisa: `Employee details: Developer with Y projects.`
Task 5 [2p]
Suprascrieti metoda `validateEmployee` in clasele copil astfel: - pentru `Manager` va verifica daca numarul de angajati este mai mare decat 1. Daca nu va afisa mesajul `Number of employees managed must be 1 or greater.`. - pentru `Developer` va verifica daca numarul de proiecte este mai mare decat 1. Daca nu va afisa mesajul `Number of projects must be 1 or greater.`.
Task 6 [2p]
Supraincarcati metoda `calculateBonus` in clasele copil astfel incat sa primeasca un parametru de tip int care sa reprezinte bonusul per angajat/proiect. Metoda va calcula astfel: - pentru `Manager` bonusul va fi salariul * 0.2 + bonus * numarul de angajati - pentru `Developer` bonusul va fi salariul * 0.15 + bonus * numarul de proiecte
Task 7 [1p]
Adugati metoda compareSalary in clasa `Employee` care va primi un obiect de tip Employee. Aceasta va returna un int astfel: - 1 daca salariul obiectului curent este mai mare decat salariul obiectului primit ca parametru - 0 daca salariul obiectului curent este egal cu salariul obiectului primit ca parametru - -1 daca salariul obiectului curent este mai mic decat salariul obiectului primit ca parametru
Mesajul de afisare: - 1 sau -1 → “Name” has a higher salary. - 0 → Both employees have the same salary.
Example:
Input: 1
Expected Output Name: John, Salary: 5000.0, Age: 35 [Position: Manager]