Interfacce

Un'interfaccia è una raccolta di dichiarazioni di metodi che consente a oggetti non correlati di comunicare tra loro. Ad esempio, ActionScript 3.0 definisce l'interfaccia IEventDispatcher, che contiene dichiarazioni di metodi utilizzabili dalle classi per gestire gli oggetti evento. L'interfaccia IEventDispatcher stabilisce una modalità standard per il passaggio degli oggetti evento da un oggetto all'altro. Il codice seguente mostra la definizione dell'interfaccia IEventDispatcher:

public interface IEventDispatcher 
{ 
    function addEventListener(type:String, listener:Function,  
            useCapture:Boolean=false, priority:int=0, 
            useWeakReference:Boolean = false):void; 
    function removeEventListener(type:String, listener:Function,  
            useCapture:Boolean=false):void; 
    function dispatchEvent(event:Event):Boolean; 
    function hasEventListener(type:String):Boolean; 
    function willTrigger(type:String):Boolean; 
}

Le interfacce si basano sulla distinzione tra l'interfaccia di un metodo e la sua implementazione. L'interfaccia di un metodo include tutte le informazioni necessarie per chiamare tale metodo, incluso il nome del metodo, tutti i suoi parametri e il tipo restituito. L'implementazione di un metodo include non solo le informazioni sull'interfaccia, ma anche le istruzioni eseguibili che attivano il comportamento del metodo. La definizione di un'interfaccia contiene soltanto interfacce di metodo e tutte le classi che implementano l'interfaccia sono responsabili della definizione delle implementazioni del metodo.

In ActionScript 3.0, la classe EventDispatcher implementa l'interfaccia IEventDispatcher mediante la definizione di tutti i metodi dell'interfaccia IEventDispatcher e l'aggiunta di corpi di metodo a ognuno dei metodi. Il codice seguente è estratto dalla definizione della classe EventDispatcher:

public class EventDispatcher implements IEventDispatcher 
{ 
    function dispatchEvent(event:Event):Boolean 
    { 
        /* implementation statements */ 
    } 
 
    ... 
}

L'interfaccia IEventDispatcher serve da protocollo per le istanze di EventDispatcher che devono elaborare oggetti evento e passarli ad altri oggetti che hanno anch'essi implementato l'interfaccia IEventDispatcher.

Per descrivere un'interfaccia è anche possibile affermare che essa definisce un tipo di dati esattamente come fa una classe. Di conseguenza, un'interfaccia può essere usata come un'annotazione di tipo, allo stesso modo di una classe. Come tipo di dati, l'interfaccia può inoltre essere utilizzata con operatori, quali is e as, che richiedono un tipo di dati. A differenza di quanto avviene per le classi, tuttavia, non è possibile creare istanze di un'interfaccia. Questa distinzione ha portato molti programmatori a considerare le interfacce come tipi di dati astratti e le classi come tipi di dati concreti.

Definizione di un'interfaccia

La struttura della definizione di un'interfaccia è simile a quella della definizione di una classe, a eccezione del fatto che l'interfaccia può contenere solo metodi e non corpi dei metodi. Le interfacce inoltre non possono includere variabili o costanti, ma possono incorporare funzioni getter e setter. Per definire un'interfaccia, utilizzate la parola chiave interface. Ad esempio, la seguente interfaccia, IExternalizable, fa parte del pacchetto flash.utils in ActionScript 3.0. L'interfaccia IExternalizable definisce un protocollo per la serializzazione di un oggetto, vale a dire la conversione di un oggetto in un formato idoneo per la memorizzazione su un dispositivo o per la trasmissione in rete.

public interface IExternalizable 
{ 
    function writeExternal(output:IDataOutput):void; 
    function readExternal(input:IDataInput):void; 
}

L'interfaccia IExternalizable viene dichiarata con il modificatore del controllo di accesso public. Le definizioni di interfaccia possono essere modificate unicamente mediante gli specificatori del controllo di accesso public e internal. Le dichiarazioni dei metodi all'interno di una definizione di interfaccia non possono avere nessuno specificatore del controllo di accesso.

ActionScript 3.0 applica una convenzione in base alla quale i nomi di interfaccia iniziano con una I maiuscola, ma è possibile utilizzare qualsiasi identificatore valido come nome di interfaccia. Le definizioni di interfaccia vengono spesso collocate nel livello più alto di un pacchetto. Le definizioni di interfaccia non possono essere collocate all'interno di una definizione di classe o di un'altra definizione di interfaccia.

Le interfacce possono estendere altre interfacce. Ad esempio, l'interfaccia IExample seguente estende l'interfaccia IExternalizable:

public interface IExample extends IExternalizable 
{ 
    function extra():void; 
}

Tutte le classi che implementano l'interfaccia IExample devono includere implementazioni non solo del metodo extra(), ma anche dei metodi writeExternal() e readExternal() ereditati dall'interfaccia IExternalizable.

Implementazione di un'interfaccia in una classe

La classe è il solo elemento del linguaggio di ActionScript 3.0 in grado di implementare un'interfaccia. Per implementare una o più interfacce, utilizzate la parola chiave implements nella dichiarazione della classe. Negli esempi seguenti vengono definite due interfacce, IAlpha e IBeta, e una classe, Alpha, che le implementa entrambe:

interface IAlpha 
{ 
    function foo(str:String):String; 
} 
 
interface IBeta 
{ 
    function bar():void; 
} 
 
class Alpha implements IAlpha, IBeta 
{ 
    public function foo(param:String):String {} 
    public function bar():void {} 
}

In una classe che implementa un'interfaccia, i metodi implementati devono:

  • Utilizzare l'identificatore del controllo di accesso public.

  • Utilizzare lo stesso nome del metodo di interfaccia.

  • Avere lo stesso numero di parametri, ciascuno con tipi di dati corrispondenti ai tipi di dati dei parametri del metodo di interfaccia.

  • Utilizzare lo stesso tipo restituito.

    public function foo(param:String):String {}

È tuttavia possibile scegliere quale nome assegnare ai parametri dei metodi implementati. Anche se il numero dei parametri e il tipo di dati di ciascun parametro del metodo implementato devono corrispondere a quelli del metodo di interfaccia, i nomi dei parametri possono essere differenti. Nell'esempio precedente, il parametro del metodo Alpha.foo() è denominato param:

Mentre è denominato str nel metodo di interfaccia IAlpha.foo():

function foo(str:String):String;

Potete disporre di una certa flessibilità anche per quanto riguarda i valori di parametro predefiniti. Una definizione di interfaccia può includere dichiarazioni di funzioni con valori di parametro predefiniti. Un metodo che implementa una tale dichiarazione di funzione deve avere un valore di parametro predefinito che sia membro dello stesso tipo di dati del valore specificato nella definizione di interfaccia, anche se il valore vero e proprio può essere differente. Ad esempio, il codice seguente definisce un'interfaccia che contiene un metodo con un valore di parametro predefinito 3:

interface IGamma 
{ 
    function doSomething(param:int = 3):void; 
}

La definizione di classe seguente implementa l'interfaccia IGamma, ma impiega un valore di parametro predefinito differente:

class Gamma implements IGamma 
{ 
    public function doSomething(param:int = 4):void {} 
}

Il motivo di questa flessibilità sta nel fatto che le regole di implementazione dell'interfaccia sono state specificamente studiate per garantire la compatibilità dei tipi di dati e per raggiungere tale obiettivo non è richiesta una corrispondenza dei nomi dei parametri e dei valori predefiniti.