Documentazione di Flash CS3 |
|||
| Programmazione in ActionScript 3.0 > Programmazione orientata agli oggetti con ActionScript > Ereditarietà | |||
L'ereditarietà è una forma di riutilizzo del codice che consente ai programmatori di sviluppare nuove classi basate sulle classi esistenti. Le classi esistenti vengono spesso definite classi base o superclassi, mentre le nuove classi sono generalmente chiamate sottoclassi. Uno dei principali vantaggi dell'ereditarietà è che consente di riutilizzare il codice di una classe di base, senza modificare il codice esistente. Inoltre, l'ereditarietà non richiede di modificare il modo in cui le altre classi interagiscono con la classe di base. Anziché modificare una classe esistente già ampiamente testata o già in uso, l'impiego dell'ereditarietà consente di trattare tale classe come un modulo integrato da estendere con proprietà o metodi aggiuntivi. Di conseguenza, la parola chiave extends viene utilizzata per indicare che una classe eredita da un'altra classe.
L'ereditarietà consente inoltre di sfruttare i vantaggi del polimorfismo all'interno del codice. Si definisce polimorfismo la capacità di usare un unico nome metodo per un metodo in grado di comportarsi in modo differente se applicato a diversi tipi di dati. Un semplice esempio è costituito da una classe base denominata Shape con due sottoclassi denominate Circle e Square. La classe Shape definisce un metodo chiamato area() che restituisce l'area della figura geometrica. Se viene implementato il polimorfismo, è possibile chiamare il metodo area() sugli oggetti di tipo Circle e Square e ottenere i calcoli corretti. L'ereditarietà abilita il polimorfismo consentendo alle sottoclassi di ereditare e ridefinire (sostituire) i metodi della classe base. Nell'esempio seguente, il metodo area() viene ridefinito dalle classi Circle e Square:
class Shape
{
public function area():Number
{
return NaN;
}
}
class Circle extends Shape
{
private var radius:Number = 1;
override public function area():Number
{
return (Math.PI * (radius * radius));
}
}
class Square extends Shape
{
private var side:Number = 1;
override public function area():Number
{
return (side * side);
}
}
var cir:Circle = new Circle();
trace(cir.area()); // output: 3.141592653589793
var sq:Square = new Square();
trace(sq.area()); // output: 1
Poiché ogni classe definisce un tipo di dati, l'uso dell'ereditarietà crea una speciale relazione tra la classe base e la classe che la estende. Una sottoclasse possiede sempre tutte le proprietà della sua classe base, di conseguenza una qualsiasi istanza di una sottoclasse può sempre essere sostituita con un'istanza della classe base. Ad esempio, se un metodo definisce un parametro di tipo Shape, è consentito il passaggio di un argomento di tipo Circle, in quanto Circle è un'estensione di Shape, come indicato di seguito:
function draw(shapeToDraw:Shape) {}
var myCircle:Circle = new Circle();
draw(myCircle);
Una proprietà di istanza, sia che venga dichiarata con la parola chiave function, var o const, viene ereditata da tutte le sottoclassi a condizione che essa non sia stata dichiarata con l'attributo private nella classe base. Ad esempio, la classe Event nell'API di Flash Player ha un numero di sottoclassi che ereditano proprietà comuni a tutti gli oggetti evento.
Per alcuni tipi di eventi, la classe Event contiene tutte le proprietà necessarie per la definizione dell'evento. Questi tipi di eventi non richiedono proprietà di istanza oltre a quelle definite nella classe Event. Esempi di tali eventi sono l'evento complete, che si verifica quando i dati vengono caricati correttamente, e l'evento connect, che si verifica quando viene stabilita una connessione di rete.
L'esempio seguente è un estratto della classe Event che illustra alcune delle proprietà e dei metodi ereditati dalle sottoclassi. Poiché le proprietà vengono ereditate, esse sono accessibili da una qualsiasi istanza di una sottoclasse.
public class Event
{
public function get type():String;
public function get bubbles():Boolean;
...
public function stopPropagation():void {}
public function stopImmediatePropagation():void {}
public function preventDefault():void {}
public function isDefaultPrevented():Boolean {}
...
}
Vi sono poi altri tipi di eventi che richiedono proprietà univoche non disponibili nella classe Event. Tali eventi vengono definiti utilizzando le sottoclassi della classe Event, in modo da consentire l'aggiunta di nuove proprietà alle proprietà già definite nella classe Event. Un esempio di tali sottoclassi è la classe MouseEvent, che aggiunge proprietà univoche per gli eventi associate ai movimenti o ai clic del mouse, quali gli eventi mouseMove e click. L'esempio seguente è un estratto della classe MouseEvent che mostra la definizione di proprietà esistenti all'interno della sottoclasse, ma non nella classe base:
public class MouseEvent extends Event
{
public static const CLICK:String = "click";
public static const MOUSE_MOVE:String = "mouseMove";
...
public function get stageX():Number {}
public function get stageY():Number {}
...
}
Se una proprietà viene dichiarata con la parola chiave public, essa risulta visibile al codice ovunque si trovi. Ciò significa che la parola chiave public, a differenza delle parole chiave private, protected e internal, non pone limiti all'ereditarietà delle proprietà.
Se una proprietà viene dichiarata con la parola chiave private, essa risulterà visibile solo nella classe che la definisce e non verrà ereditata da alcuna delle sottoclassi. Ciò non accadeva nelle versioni precedenti di ActionScript, dove la parola chiave private si comportava in modo simile alla parola chiave protected di ActionScript 3.0.
La parola chiave protected indica che una proprietà è visibile non solo all'interno della classe che la definisce, ma anche in tutte le sue sottoclassi. A differenza della parola chiave protected del linguaggio di programmazione Java, la parola chiave protected di ActionScript 3.0 non rende una proprietà visibile a tutte le altre classi dello stesso pacchetto. In ActionScript 3.0, solo le sottoclassi possono accedere a una proprietà dichiarata con la parola chiave protected. Inoltre, una proprietà protetta è visibile a una sottoclasse, sia che questa si trovi nello stesso pacchetto della classe base che in un pacchetto differente.
Per limitare la visibilità di una proprietà al pacchetto nel quale essa è stata definita, utilizzare la parola chiave internal oppure non utilizzare alcuno specificatore del controllo di accesso. Lo specificatore del controllo di accesso internal è lo specificatore predefinito che viene applicato quando non ne viene specificato alcuno. Se una proprietà viene contrassegnata come internal essa verrà ereditata unicamente dalle sottoclassi che risiedono nello stesso pacchetto.
L'esempio seguente mostra come ogni specificatore del controllo di accesso può modificare l'ereditarietà nell'ambito dei pacchetti. Il codice seguente definisce una classe di applicazione principale chiamata AccessControl e due altre classi chiamate Base ed Extender. La classe Base si trova in un pacchetto denominato foo, mentre la classe Extender, che è una sottoclasse della classe Base, si trova in un pacchetto chiamato bar. La classe AccessControl importa unicamente la classe Extender e crea un'istanza di Extender che tenta di accedere a una variabile chiamata str definita nella classe Base. La variabile str è dichiarata come public, di conseguenza il codice viene compilato ed eseguito come nell'estratto seguente:
// Base.as in una cartella chiamata foo
package foo
{
public class Base
{
public var str:String = "hello"; // modifica public in questa riga
}
}
// Extender.as in una cartella chiamata bar
package bar
{
import foo.Base;
public class Extender extends Base
{
public function getString():String {
return str;
}
}
}
// classe applicazione principale in file chiamato ProtectedExample.as
import flash.display.MovieClip;
import bar.Extender;
public class AccessControl extends MovieClip
{
public function AccessControl()
{
var myExt:Extender = new Extender();
trace(myExt.testString); // errore se str non è public
trace(myExt.getString()); // errore se str è private o internal
}
}
}
Per vedere come gli altri specificatori del controllo di accesso possono modificare la compilazione e l'esecuzione dell'esempio precedente, modificare lo specificatore del controllo di accesso alla variabile str in private, protected o internal, dopo avere eliminato o escluso mediante commento la seguente riga dalla classe AccessControl:
trace(myExt.testString); // errore se str non è public
Le proprietà dichiarate con le parole chiave var o const vengono ereditate, ma non possono essere sostituite. Sostituire una proprietà significa ridefinirla in una sottoclasse. Il solo tipo di proprietà che è possibile sostituire sono i metodi, vale a dire le proprietà dichiarate con la parola chiave function. Nonostante non sia possibile sostituire una variabile di istanza, si può ottenere un risultato simile mediante la creazione di metodi getter e setter per la variabile di istanza, quindi sostituire tali metodi. Per ulteriori informazioni, vedere Sostituzione di getter e setter.
Sostituire un metodo significa ridefinire il comportamento di un metodo ereditato. I metodi statici non vengono ereditati e non possono essere sostituiti. I metodi di istanza, tuttavia, vengono ereditati dalle sottoclassi e possono essere sostituiti a condizione che vengano soddisfatti i seguenti criteri:
final nella classe base. Se utilizzata con un metodo di istanza, la parola chiave final indica l'intenzione del programmatore di impedire alle sottoclassi di sostituire il metodo.private nella classe base. Se un metodo viene contrassegnato come private nella classe base, non è necessario utilizzare la parola chiave override per la definizione di un metodo con nome identico nella sottoclasse, in quanto il metodo della classe base non risulterà visibile alla sottoclasse.Per sostituire un metodo di istanza che soddisfi i criteri di cui sopra, è necessario che nella definizione del metodo all'interno della sottoclasse venga utilizzata la parola chiave override e che tale definizione corrisponda alla versione del metodo della superclasse, nei modi indicati di seguito:
I nomi dei parametri del metodo di sostituzione, tuttavia, non devono corrispondere ai nomi dei parametri del metodo della classe base, a condizione che il numero dei parametri e il tipo di dati di ciascun parametro corrisponda.
Quando sostituiscono un metodo, i programmatori spesso intendono aggiungere qualcosa al comportamento del metodo della superclasse da sostituire, anziché rimpiazzare completamente tale comportamento. Ciò richiede un meccanismo che consenta a un metodo in una sottoclasse di richiamare la versione di se stesso presente nella superclasse. L'istruzione super consente tale operazione, in quanto contiene un riferimento all'immediata superclasse. Nell'esempio seguente viene definita una classe chiamata Base contenente un metodo chiamato thanks() e una sottoclasse della classe Base denominata Extender che sostituisce il metodo thanks(). Il metodo Extender.thanks() impiega l'istruzione super per chiamare Base.thanks().
package {
import flash.display.MovieClip;
public class SuperExample extends MovieClip
{
public function SuperExample()
{
var myExt:Extender = new Extender()
trace(myExt.thanks()); // output: Mahalo nui loa
}
}
}
class Base {
public function thanks():String
{
return "Mahalo";
}
}
class Extender extends Base
{
override public function thanks():String
{
return super.thanks() + " nui loa";
}
}
Nonostante non sia possibile sostituire le variabili definite in una superclasse, è invece possibile sostituire le funzioni getter e setter. Ad esempio, il codice seguente consente di sostituire una funzione getter denominata currentLabel definita nella classe MovieClip dell'API di Flash Player.
package
{
import flash.display.MovieClip;
public class OverrideExample extends MovieClip
{
public function OverrideExample()
{
trace(currentLabel)
}
override public function get currentLabel():String
{
var str:String = "Override: ";
str += super.currentLabel;
return str;
}
}
}
Il risultato dell'istruzione trace() nella funzione di costruzione classe OverrideExample è Override: null, a indicare che nell'esempio è stato possibile sostituire la proprietà ereditata currentLabel.
Le proprietà statiche non vengono ereditate dalle sottoclassi. Ciò significa che non è possibile accedere alle proprietà statiche attraverso un'istanza di una sottoclasse. Una proprietà statica è accessibile unicamente attraverso l'oggetto di classe sul quale viene definita. Ad esempio, il codice seguente definisce una classe base chiamata Base e una sottoclasse che la estende denominata Extender. Nella classe Base viene definita una variabile statica chiamata test. Il codice riportato nell'estratto seguente non viene compilato in modalità rigorosa e genera un errore di runtime in modalità standard.
package {
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
trace(myExt.test); // Errore
}
}
}
class Base {
public static var test:String = "static";
}
class Extender extends Base { }
Il solo modo per accedere alla variabile statica test è mediante l'oggetto di classe, come illustrato nel codice seguente:
Base.test;
È tuttavia consentito definire una proprietà di istanza con lo stesso nome della proprietà statica. Tale proprietà di istanza può essere definita nella stessa classe della proprietà statica o in una sua sottoclasse. Ad esempio, la classe Base dell'esempio precedente potrebbe avere una proprietà di istanza denominata test. Il codice seguente viene compilato ed eseguito perché la proprietà di istanza viene ereditata dalla classe Extender. Il codice verrebbe compilato ed eseguito anche se la definizione della variabile di istanza test venisse spostata, ma non copiata, nella classe Extender.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
trace(myExt.test); // output: istanza
}
}
}
class Base
{
public static var test:String = "static";
public var test:String = "instance";
}
class Extender extends Base {}
Nonostante le proprietà statiche non vengano ereditate, esse rientrano in una catena delle aree di validità della classe che definisce tali proprietà e tutte le sottoclassi della classe. Di conseguenza, le proprietà statiche vengono dette nell'area di validità della classe nella quale vengono definite e di tutte le sue sottoclassi. Ciò significa che una proprietà statica risulta direttamente accessibile all'interno del corpo della classe che definisce la proprietà statica e tutte le sottoclassi della classe.
Nell'esempio che segue vengono modificate le classi definite nell'esempio precedente, al fine di mostrare come la variabile statica test definita nella classe Base si trovi nell'area di validità della classe Extender. In altre parole, la classe Extender può accedere alla variabile statica test senza aggiungervi un prefisso corrispondente al nome della classe che definisce test.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
}
}
}
class Base {
public static var test:String = "static";
}
class Extender extends Base
{
public function Extender()
{
trace(test); // output: static
}
}
Se viene definita una proprietà di istanza che impiega lo stesso nome di una proprietà statica presente nella stessa classe o in una superclasse, la proprietà di istanza ha una precedenza maggiore nella catena delle aree di validità. La proprietà di istanza prevarica la proprietà statica, ovvero il valore della proprietà di istanza viene utilizzato al posto del valore della proprietà statica. Ad esempio, il codice seguente mostra che, se la classe Extender definisce una variabile di istanza denominata test, l'istruzione trace() impiega il valore della variabile di istanza anziché quello della variabile statica.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
}
}
}
class Base
{
public static var test:String = "static";
}
class Extender extends Base
{
public var test:String = "instance";
public function Extender()
{
trace(test); // output: istanza
}
}
Flash CS3
Inviami un messaggio e-mail quando vengono aggiunti dei commenti a questa | Rapporto sui commenti
Pagina corrente: http://livedocs.adobe.com/flash/9.0_it/main/00000068.html