Come tutti i buoni linguaggi ad oggetti, anche PHP permette di estendere una classe tramite altre classi. Avviso subito che assumo che si sappia il significato dell’ereditarietà tra classi. Spiegare questo concetto richiederebbe molto tempo, ed esula quindi dallo scopo di questo articolo.
Estendere una classe: keyword extends
Come in Java, per creare una classe che estende una classe già esistente si utilizza la parola chiave extends. Ecco subito un esempio.
<?php
// Definizione della classe Animale
class Animale {
private $specie;
// Costruttore
public function __construct($specie){
$this->specie = $specie;
}
// Ritorna la specie
public function getSpecie(){
return $this->specie;
}
// Imposta la specie
public function setSpecie($specie){
$this->specie = $specie;
}
}
// Definizione della classe Mammifero
class Mammifero extends Animale {
private $corna;
public function __construct($specie){
$this->setSpecie($specie);
}
public function haCorna(){
return $this->corna;
}
public function setCorna($corna){
$this->corna = $corna;
}
}
// Definizione della classe Uccello
class Uccello extends Animale {
private $rapace;
public function __construct($specie){
$this->setSpecie($specie);
}
public function isRapace(){
return $this->rapace;
}
public function setRapace($rapace){
$this->rapace = $rapace;
}
}
?>
Nell’esempio abbiamo una classe Animale, e due sue specializzazioni Mammifero e Uccello. Come in tutti gli altri linguaggi ad oggetti, le sottoclassi Mammifero e Uccello, dichiarate con extends, ereditano tutti i metodi e gli attributi della classe genitore. Quindi sia Mammifero che Uccello avranno i metodi setSpecie() e getSpecie() e l’attributo $specie. Inoltre in Mammifero è possibile impostare se l’animale ha o meno le corna, mentre in Uccello è possibile impostare se l’animale è o meno un rapace. Evidentemente questi metodi sono specifici di mammiferi e uccelli, e quindi sarebbe scorretto inserire queste funzionalità nella classe Animale.
Ecco come è possibile usare tali classi:
<?php
// Creo un animale
$anim = new Animale("giraffa"); // Cambio da giraffa a leone
$anim->setSpecie("Leone");
// Tento di impostare che il leone non ha corna
$anim->setCorna(false); // Errore!!! setCorna() è di Mammifero, non di Animale
// Creo il leone come Mammifero
$mamm = new Mammifero("leone");
$mamm->setCorna(false); // Ok
// Cambio da leone a cervo
$mamm->setSpecie("cervo"); // Ok, setSpecie() ereditato
// Il cervo ha le corna
$mamm->setCorna(true); // Ok
// Creo un uccello
$ucc = new Uccello("gufo");
// Il gufo non ha corna
$ucc->setCorna(false); // Errore! setCorna() non è di Uccello (non esiston uccelli con le corna)
// Il gufo non è rapace
$ucc->setRapace(false); // Ok
?>
Visibilità dei metodi: il modificatore protected
Come abbiamo visto, una sottoclasse eredita tutti i metodi e attributi della classe genitore. Tuttavia all’esterno rimarranno visibili solo i metodi e gli attributi public, sia quelli ereditati che quelli definiti nella sottoclasse.
All’interno della sottoclasse è invece possibile usare tutto ciò che è stato dichiarato public o protected nella classe base. Tutto ciò che era private non è quindi visibile nella sottoclasse. E’ per questo motivo che nel costruttore delle sottoclassi Mammifero e Uccello è necessario usare setSpecie() per impostare la specie. L’attributo $specie infatti non è visibile, poichè è private.
La visibilità protected si trova a metà strada tra public e private. In pratica ciò che è protected è utilizzabile nella classe in cui è dichiarato e in tutte le sottoclassi, ma non all’esterno della classe. Usando questo tipo di visibilità è possibile modificare le classi in questo modo (ometto il codice inutile e la classe Uccello):
<?php
// Definizione della classe Animale
class Animale {
protected $specie;
// Costruttore
public function __construct($specie){
$this->specie = $specie;
}
// ... resto della classe ...
}
// Definizione della classe Mammifero
class Mammifero extends Animale {
private $corna;
public function __construct($specie){
$this->specie = $specie;
}
// ... resto della classe ...
}
?>
E’ chiaro che impostando $specie come protected possiamo accedere all’attributo direttamente anche in Mammifero e Uccello. Quindi i costruttori di queste due classi possono impostare l’attributo direttamente, senza ricorrere a setSpecie().
Ovviamente è possibile avere ereditarietà multiple, ovvero avere classi che estendono una classe che a sua volta ne estende un’altra, e così via. Non è invece possibile l’ereditarietà multipla alla maniera di C++, ovvero non è possibile avere una classe che estende contemporaneamente più di una classe. D’altronde ciò non è possibile nemmeno in linguaggi altrettanto potenti, come Java.
In un prossimo articolo spenderemo due parole sull’ereditariet? del costruttore e del distruttore, daremo spazio al concetto di overriding di metodi, e spiegheremo la keyword final.