Ce face Polimorfism
O variabilă de tipul superclasei sau al unei interfețe poate ține obiecte din orice subclasă. Când apelezi o metodă rescrisă, Java alege la rulare versiunea din clasa reală a obiectului (legare dinamică). Codul care lucrează cu Forma nu trebuie să știe dacă are un cerc sau un dreptunghi.
Compilatorul se uită doar la tipul variabilei: prin Forma f poți apela numai metodele declarate în Forma. Pentru metodele specifice subclasei ai nevoie de o conversie, de preferat cu instanceof cu pattern.
Supraîncărcarea (overloading, metode cu același nume și parametri diferiți) este altceva: versiunea se alege la compilare, după tipul declarat al argumentelor. Rescrierea (overriding) se alege la rulare, după obiect.
Exemple
Exemplul 1: Același apel, comportamente diferite
Bucla nu știe ce formă are în față. Fiecare obiect răspunde cu propria metodă arie().
abstract class Forma {
abstract double arie();
String nume() {
return getClass().getSimpleName();
}
}
class Cerc extends Forma {
final double raza;
Cerc(double raza) { this.raza = raza; }
@Override
double arie() { return Math.PI * raza * raza; }
}
class Dreptunghi extends Forma {
final double l, h;
Dreptunghi(double l, double h) { this.l = l; this.h = h; }
@Override
double arie() { return l * h; }
}
class Patrat extends Dreptunghi {
Patrat(double latura) { super(latura, latura); }
}
public class Main {
public static void main(String[] args) {
Forma[] forme = {new Cerc(1.5), new Dreptunghi(3, 4), new Patrat(2.5)};
double total = 0;
for (Forma f : forme) {
System.out.printf("%-10s %8.2f m²%n", f.nume(), f.arie());
total += f.arie();
}
System.out.printf("%-10s %8.2f m²%n", "Total", total);
}
}
Rezultat
Cerc 7.07 m²
Dreptunghi 12.00 m²
Patrat 6.25 m²
Total 25.32 m²
Exemplul 2: Rescriere (la rulare) și supraîncărcare (la compilare)
Variabila n are tipul Notificare, dar obiectul este Sms: trimite() alege versiunea din Sms. Metoda supraîncărcată descrie se alege după tipul variabilei, deci se apelează varianta pentru Notificare.
class Notificare {
String trimite() { return "notificare generică"; }
}
class Sms extends Notificare {
@Override
String trimite() { return "SMS către 07xx xxx xxx"; }
}
class Email extends Notificare {
@Override
String trimite() { return "email către ana@exemplu.ro"; }
}
public class Main {
static String descrie(Notificare n) { return "descrie(Notificare)"; }
static String descrie(Sms s) { return "descrie(Sms)"; }
public static void main(String[] args) {
Notificare n = new Sms();
System.out.println(n.trimite());
System.out.println(descrie(n));
System.out.println(descrie(new Sms()));
Notificare[] toate = {new Notificare(), new Sms(), new Email()};
for (Notificare x : toate) {
System.out.println("-> " + x.trimite());
}
}
}
Rezultat
SMS către 07xx xxx xxx
descrie(Notificare)
descrie(Sms)
-> notificare generică
-> SMS către 07xx xxx xxx
-> email către ana@exemplu.ro
Greșeli frecvente
- Apelezi prin variabila superclasei o metodă care există doar în subclasă:
forma.getRaza()dăcannot find symbol. Verifică tipul cuif (forma instanceof Cerc c)și foloseștec. - Te aștepți ca metodele
staticsă fie polimorfe. Ele nu se rescriu, ci se ascund: se alege versiunea după tipul variabilei, nu al obiectului. - Confunzi supraîncărcarea cu rescrierea. Dacă parametrul diferă, nu ai rescris nimic;
@Overridete avertizează.
Comenzi înrudite
Moștenire (extends): Cu extends o clasă moștenește câmpurile și metodele altei clase și le poate extinde sau rescrie; Java permite o singură superclasă.Interfețe (interface, implements): O interfață (interface) descrie ce metode trebuie să aibă o clasă; clasa o promite cu implements și poate implementa mai multe interfețe.Clase abstracte (abstract): O clasă abstract nu poate fi instanțiată direct; poate avea metode abstracte, pe care subclasele concrete sunt obligate să le scrie.instanceof: instanceof verifică dacă un obiect este de un anumit tip; în forma nouă declară și o variabilă deja convertită la acel tip.Pattern matching (instanceof și switch): Pattern matching testează tipul (și forma) unui obiect și îi extrage părțile într-un singur pas, în instanceof sau în switch.