Salve a tutti !
Eccoci arrivati al capitolo 9 della
Programmazione Orientata agli Oggetti con Flash CS3.
Il tema di questo tutorial è l' Ereditarietà di Actionscript 3.0 .
Tengo a precisare che questo tema è di certo un tema molto vasto e complesso e non è il fine di FlepStudio entrare nei minimi particolari.
Questo tutorial vuole dare una prima ' infarinatura ' su quello che è l' Ereditarietà ed esprimere i suoi concetti primari.
Per chi volesse approfondire l' argomento, consiglio vivamente il seguente libro di testo:
-
Essential Actionscript 3.0 di Colin Moock editore O'REILLY
Detto questo, cosa significa Ereditarietà in Actionscript 3.0 ?
Nella Programmazione Orientata agli Oggetti, il termine Ereditarietà esprime il concetto di relazione tra 2 o più classi, dove una classe eredita le definizioni di proprietà e metodi di un' altra classe.
In termini pratici, l' Ereditarietà permette semplicemente di usare il codice di una classe in un' altra classe.
Ereditarietà in biologia è un processo genetico per il quale un figlio eredita ad esempio il colore degli occhi del padre ed il colore dei capelli della madre.
Questo processo non implica che il figlio ha occhi e capelli identici ai genitori, bensì avrà anche sue particolarità.
In Actionscript 3.0 l' Ereditarietà ha una collocazione molto simile a questo paragone, quindi una classe avrà a disposizione metodi e proprietà di un' altra classe e in più implementerà dei suoi metodi e delle sue proprietà.
Vediamo degli esempi concreti...
Creo un FLA che salvo con nome 'main.fla' .
Creo la Document Class, un file AS che salvo con nome Main.as, implementata in questo modo:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
}
}
}
Nel primo tutorial della Programmazione Orientata agli Oggetti avevo saltato la spegazione dell' operatore extends.
Ora è giunto il momento di svelare questo arcaico segreto !
La parola extends fa capire a Flash che la nostra classe Main estende un' altra classe, in questo caso la classe MovieClip.
Estendere una classe significa ereditare le sue proprietà ed i suoi metodi.
In gergo di programmazione, in questo caso possiamo tranquillamente affermare che la classe MovieClip è la superclass di Main e Main è la subclass di MovieClip.
Non lasciamoci però ingannare dai termini superclass e subclass perchè grammaticalmente parlando sembrerebbe che la MovieClip essendo la superclass di Main sia la classe più completa e invece è il contrario. La subclass è sempre la classe più completa perchè ha a disposizione metodi e proprietà della sua superclass e in più può implemetarne altri solo suoi.
Infatti, la classe MovieClip in questo esempio non eredita dalla classe Main, in quanto è Main che estende MovieClip, è Main figlia di MovieClip... da quando in quà un padre eredita da un figlio...mai !
Facciamo un esempio, dato che main è subclass di MovieClip dovrebbe avere a disposizione le proprietà della classe MovieClip. Vero ! Infatti se proviamo a chiadere a flash la proprietà alpha di Main:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
trace(alpha);
}
}
}
Flash non ci avvisa con un errore che non esiste alcuna proprietà con nome alpha in Main.as, anzi... ci da questo output:
1
Questo significa che Main ha ereditato la proprietà alpha dalla classe MovieClip.
Infatti se facciamo un altro esempio con la x, y e rotation:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
trace(x);
trace(y);
trace(rotation);
}
}
}
anche in questo caso non ci arriva un errore, bensì il seguente output:
0
0
0
Ora facciamo un altro esempio con due classi che vengono istanzate dalla Document Class.
Creo una classe che chiamo Aclass, quindi un file AS che salvo con nome 'Aclass.as' implementato in questo modo:
Code:
package
{
public class Aclass
{
public function Aclass()
{
}
}
}
gli implemento anche una prorprietà ( my_name ) ed un metodo ( eyes ):
Code:
package
{
public class Aclass
{
public var my_name:String='A';
public function Aclass()
{
}
public function eyes():void
{
trace('eyes method of Aclass has ben invoked');
}
}
}
Adesso istanzio la classe dalla Document Class ( Main ):
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var a_class:Aclass=new Aclass();
trace(a_class.my_name);
a_class.eyes();
}
}
}
e fin quì niente di nuovo, infatti ho istanziato Aclass ed ho chiamato la sua proprietà my_name ed il suo metodo eyes ottenendo il seguente output:
A
eyes method of Aclass has ben invoked
Ora creo un' altra classe che si chiama Bclass e che estende Aclass, quindi un file AS che salvo con nome ' Bclass.as ', implementato in questo modo:
Code:
package
{
public class Bclass extends Aclass
{
public function Bclass()
{
}
}
}
Per i motivi che ho già detto introducendo l' Ereditarietà, Bclass extends Aclass significa che Bclass può usufruire dei metodi e delle proprietà di Aclass.
Infatti faccio subito una prova, istanzio Bclass e chiamo la proprietà my_name ed il metodo eyes dalla sua istanza:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var a_class:Aclass=new Aclass();
trace(a_class.my_name);
a_class.eyes();
var b_class:Bclass=new Bclass();
trace(b_class.my_name);
b_class.eyes();
}
}
}
ottenendo questo output:
A
eyes method of Aclass has ben invoked
A
eyes method of Aclass has ben invoked
ECCOCI al succo ! Nonostante Bclass non ha implementato la proprietà my_name e tantomeno il metodo eyes, Flash legge che estende Aclass e quindi va a cercare tale proprietà e tale metodo in Aclass !
Capire a fondo quello che è successo è di fondamentale importanza per chi di voi volesse imparare a sviluppare rispettando le buone norme e regole della OOP.
Importante: una classe Actionscript 3.0 non può ereditare proprietà e metodi statici dalla sua superclass.
Vediamo ora un altro esempio.
Se avessimo del codice nella funzione costruttrice di Aclass ( che come sappiamo viene eseguito immediatamente al momento in cui viene istanziata la classe ), Bclass eredita tale codice anche nella propria funzione costruttrice ?
Implemento del codice nella funzione costruttrice di Aclass in questo modo:
Code:
package
{
public class Aclass
{
public var my_name:String='A';
public function Aclass()
{
trace('Aclass has been instantiate');
}
public function eyes():void
{
trace('eyes method of Aclass has ben invoked');
}
}
}
se pubblico il FLA ottengo il seguente output:
Aclass has been instantiate
A
eyes method of Aclass has ben invoked
Aclass has been instantiate
A
eyes method of Aclass has ben invoked
Come possiamo vedere, Bclass ha ereditato anche il codice implementato nella funzone costruttrice di Aclass.
In questo caso, Flash aggiunge implicitamente una chiamata alla funzione costruttrice della superclass. aggiungendo la chiave super()
Però il modo più ' educato ' è chiamare la funzione costruttrice di Aclass da Bclass utilizzando questa chiave super() noi stessi, in questo modo:
Code:
package
{
public class Bclass extends Aclass
{
public function Bclass()
{
super();
}
}
}
così chi dovesse leggere la nostra classe capisce subito che sappiamo quello che stiamo facendo senza dare l' ombra del minimo dubbio che non sapessimo ciò che veramente accade.
Fino ad ora abbiamo visto come usufruire dei metodi di Aclass anche con Bclass. Naturalmente Bclass a sua volta può implementare dei suoi metodi.
Per esempio, implemento un nuovo metodo ( hairs ) a Bclass:
Code:
package
{
public class Bclass extends Aclass
{
public function Bclass()
{
super();
}
public function hairs():void
{
trace('hairs method of Bclass has been invoked');
}
}
}
Ora dalla Document Class, in cui risiedono le istanze di Aclass e Bclass, provo a chiamare tale metodo:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var a_class:Aclass=new Aclass();
trace(a_class.my_name);
a_class.eyes();
var b_class:Bclass=new Bclass();
trace(b_class.my_name);
b_class.eyes();
b_class.hairs();
}
}
}
ottenendo questo output
Aclass has been instantiate
A
eyes method of Aclass has ben invoked
Aclass has been instantiate
A
eyes method of Aclass has ben invoked
hairs method of Bclass has been invoked
e quindi notiamo che il metodo è stato eseguito regolarmente. Questo ci da la certezza che la subclass oltre a poter usufruire dei metodi della superclass, puà avere dei suoi metodi.
Attenzione però... ricordiamoci che un padre ( Aclass ) non può ereditare da un figlio ( Bclass ), infatti se dovessimo provare a chiamare il metodo hairs di Bclass dalla istanza di Aclass:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var a_class:Aclass=new Aclass();
trace(a_class.my_name);
a_class.eyes();
var b_class:Bclass=new Bclass();
trace(b_class.my_name);
b_class.eyes();
b_class.hairs();
}
}
}
ricevremmo il seguente errore:
1061: Call to a possibly undefined method hairs through a reference with static type Aclass.
perchè Flash non trova alcun metodo hairs in Aclass e di certo non va a cercarlo nel figlio ( Bclass )... è sempre il figlio ( la subclass ) che eredita e mai il contrario.
Esaminiamo ora un altro importante caso.
Se volessimo che Bclass ereditasse il metodo eyes da Aclass, abbiamo visto come fare. Ma se volessimo che lo stesso metodo oltre ad eseguire il codice presente nel metodo eyes appunto di Aclass eseguisse anche un' altra azione ?
Alle volte capita che una subclass ha necessità di ereditare dalla superclass per non stare a riscrivere tutto il codice...ma... diciamo che non gli basta... che vuole che esegua quel codice ma anche un' altra azione...
Semplice ! Basta sovrascrivere il metodo della superclass nella subclass.
Confusione? Vediamo un esempio pratico.
Sovrascrivo il metodo eyes in Bclass:
Code:
package
{
public class Bclass extends Aclass
{
public function Bclass()
{
super();
}
public function hairs():void
{
trace('hairs method of Bclass has been invoked');
}
override public function eyes():void
{
trace('override eyes method of Bclass has ben invoked');
}
}
}
override public function eyes, significa: Flash ! sovrascrivi il metodo eyes di Bclass che lo eredita da Aclass e quindi esegui il suo codice... non andare a cercare questo metodo da Aclass !
Infatti mantenendo le stesse chiamate dalle istanze di Aclass e Bclass in Main, ottengo questo output:
Aclass has been instantiate
A
eyes method of Aclass has ben invoked
Aclass has been instantiate
A
override eyes method of Bclass has ben invoked
hairs method of Bclass has been invoked
Come possiamo leggere da questo output, l' istanza b_class di Bclass ha invocato il metodo eyes e dato che Bclass implementa un metodo sovrascritto, viene eseguito quello.
Importante: una classe Actionscript 3.0 non può sovrascrivere proprietà, metodi statici e proprietà statiche.
Infine vediamo cosa succede se la funzione costruttrice della superclass ( Aclass ) implementa dei parametri.
Mettiamo il caso che abbiamo una classe chiamata Cclass, quindi un file AS che salvo con nome ' Cclass.as ', implementato in questo modo:
Code:
package
{
public class Cclass
{
public function Cclass(a:int,b:String)
{
trace('parameter a of Cclass is: '+a,'parameter b of Cclass is: '+b);
}
}
}
come possiamo noare, Cclass ha 2 parametri implementati nella funzione costruttrice, quindi possiamo istanziare Cclass da Main in questo modo:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var c_class:Cclass=new Cclass(2,'hello');
}
}
}
ecco l' output che otteniamo:
parameter a of Cclass is: 2 parameter b of Cclass is: hello
e fin quì niente di nuovo.
Vediamo se vogliamo una classe che estende Cclass, come implementare la sua funzione costruttrice. Creo una classe con con nome Dclass, quindi un file AS che salvo con nome ' Dclass.as ' implementato in questo modo:
Code:
package
{
public class Dclass extends Cclass
{
public function Dclass()
{
}
}
}
Ricapitolando... Dclass estende Cclass, però Cclass implementa 2 parametri nella funzione costruttrice mentre Dclass no. Se provo ad istanziare Dclass:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var c_class:Cclass=new Cclass(2,'hello');
var d_class:Dclass=new Dclass();
}
}
}
ottengo questo errore:
1203: No default constructor found in base class Cclass.
Questo significa che Dclass deve avere anche lei i 2 parametri dichiarati nella funzione costruttrice. Ma proviamo ad istanziarla passandogli i parametri anche se non sono stati dichiarati:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var c_class:Cclass=new Cclass(2,'hello');
var d_class:Dclass=new Dclass(4,'bye bye');
}
}
}
niente da fare... ottengo lo stesso errore:
1203: No default constructor found in base class Cclass.
A questo punto, dichiaro i 2 parametri in Dclass:
Code:
package
{
public class Dclass extends Cclass
{
public function Dclass(a:int,b:String)
{
trace('parameter a of Dclass is: '+a,'parameter b of Dclass is: '+b);
}
}
}
adesso rirpovo ad istanziare Dclass
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var c_class:Cclass=new Cclass(2,'hello');
var d_class:Dclass=new Dclass(4,'bye bye');
}
}
}
e anche questa volta... niente da fare. Ottengo lo stesso errore:
1203: No default constructor found in base class Cclass.
Questo perchè Flash aggiunge implicitamente super() ma non gli aggiunge i parametri, quindi viene chiamata la costruttrice di Cclass che però vuole 2 parametri e quindi Flash ci da un errore.
Ecco perchè bisogna sempre abituarsi ad usare la chiave super().
Proviamo ad aggiungerla nella funzione costruttrice di Dclass passando anche i 2 parametri, in questo modo:
Code:
package
{
public class Dclass extends Cclass
{
public function Dclass(a:int,b:String)
{
super(a,b);
trace('parameter a of Dclass is: '+a,'parameter b of Dclass is: '+b);
}
}
}
Istanzio Dclass:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var c_class:Cclass=new Cclass(2,'hello');
var d_class:Dclass=new Dclass(4,'bye bye');
}
}
}
e questa volta ottengo il seguente output:
parameter a of Cclass is: 2 parameter b of Cclass is: hello
parameter a of Cclass is: 4 parameter b of Cclass is: bye bye
parameter a of Dclass is: 4 parameter b of Dclass is: bye bye
Studiamo questo output.
Prima istanzio Cclass e infatti mi dice:
parameter a of Cclass is: 2 parameter b of Cclass is: hello
poi istanzio Dclass e mi dice:
parameter a of Cclass is: 4 parameter b of Cclass is: bye bye, e questo mi fa capire che prima è stata richiamata la funzione costruttrice di Cclass ( dato che c'è super nella costruttrice di Dclass )
e poi è stata chiamata la costruttrice di Dclass, infati mi dice:
parameter a of Dclass is: 4 parameter b of Dclass is: bye bye
A questo punto, una domanda mi sorge spontanea...e se volessi aggiugere un parametro alla subclass ma non alla superclass ?
Allora la risposta corretta è: prima di tutto, la subclass deve dichiarare gli stessi parametri dichiarati nella sua superclass e poi posso implementare altri parametri.
Quindi, la costruttrice di Dclass diventa così:
Code:
package
{
public class Dclass extends Cclass
{
public function Dclass(a:int,b:String,c:Number)
{
super(a,b);
trace('parameter a of Dclass is: '+a,'parameter b of Dclass is: '+b,'parameter c of Dclass is: '+c);
}
}
}
e infatti se istanzio Dclass passandogli 3 parametri:
Code:
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
var c_class:Cclass=new Cclass(2,'hello');
var d_class:Dclass=new Dclass(4,'bye bye',3.7);
}
}
}
ottengo questo output:
parameter a of Cclass is: 2 parameter b of Cclass is: hello
parameter a of Cclass is: 4 parameter b of Cclass is: bye bye
parameter a of Dclass is: 4 parameter b of Dclass is: bye bye parameter c of Dclass is: 3.7
Infatti questa volta notiamo che:
Prima istanzio Cclass e infatti mi dice:
parameter a of Cclass is: 2 parameter b of Cclass is: hello
poi istanzio Dclass e mi dice: parameter a of Cclass is:
4 parameter b of Cclass is: bye bye, e questo mi fa capire che prima è stata richiamata la funzione costruttrice di Cclass ( dato che c'è super nella costruttrice di Dclass ) passandogli però solo i primi 2 parametri ( esattamente quelli che anche Cclass implementa )
e poi è stata chiamata la costruttrice di Dclass utilizzando i 3 parametri, infatti mi dice:
parameter a of Dclass is: 4 parameter b of Dclass is: bye bye parameter c of Dclass is: 3.7
Allego i files: