Interfejsy

Interfejs jest to kolekcja deklaracji metod umożliwiająca wzajemną komunikację między niepowiązanymi ze sobą obiektami. Na przykład w języku ActionScript 3.0 zdefiniowany jest interfejs IEventDispatcher zawierający deklaracje metod, których klasy mogą używać do obsługi obiektów zdarzeń. Interfejs IEventDispatcher udostępnia jednolity sposób przekazywania obiektów zdarzeń między obiektami. Poniższej przedstawiono definicję interfejsu 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; 
}

Interfejsy bazują na koncepcji rozdzielenia interfejsu metody od jej implementacji. Interfejs metody zawiera wszystkie informacje potrzebne do wywołania metody, w tym jej nazwę, wszystkie parametry oraz typ zwracanej wartości. Implementacje metody zawiera nie tylko informacje podane w interfejsie, lecz również instrukcje wykonywalne, które realizują zachowanie metody. Definicja interfejsu zawiera tylko interfejsy metod, a każda klasa implementująca interfejs musi definiować implementacje metod.

W języku ActionScript 3.0 klasa EventDispatcher implementuje interfejs IEventDispatcher, definiując wszystkie metody interfejsu IEventDispatcher oraz dodając treści tych metod. Poniższy kod stanowi wycinek z definicji klasy EventDispatcher:

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

Interfejs IEventDispatcher pełni rolę protokołu, zgodnie z którym instancje klasy EventDispatcher przetwarzają obiekty zdarzeń i przekazują je do innych obiektów, które także implementują interfejs IEventDispatcher.

O interfejsie możemy też powiedzieć, że definiuje typ danych, podobnie jak klasa. W związku z tym nazwy interfejsu można użyć w charakterze wskazania typu danych, tak samo jak klasy. Jako typ danych interfejs może być również używany z operatorami, takimi jak is oraz as , które jako operandu wymagają typu danych. Jednak w odróżnieniu od klasy interfejs nie może posłużyć do utworzenia instancji. To rozróżnienie sprawia, że wielu programistów postrzega interfejsy jako abstrakcyjne typy danych, zaś klasy jako konkretne typy danych.

Definiowanie interfejsu

Struktura definicji interfejsu przypomina strukturę definicji klasy, z tym że interfejs może zawierać tylko deklaracje metod, a nie ich treści. Interfejsy nie mogą zawierać zmiennych ani stałych, ale mogą zawierać metody ustawiające i pobierające. Do definiowania interfejsów służy słowo kluczowe interface . Na przykład: interfejs IExternalizable jest częścią pakietu flash.utils w języku ActionScript 3.0. Interfejs IExternalizable definiuje protokół dla szeregowania obiektu, co oznacza konwertowanie obiektu na format odpowiedni od zapisu na urządzeniu lub do przesyłania w sieci.

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

Interfejs IExternalizable został zadeklarowany ze specyfikatorem dostępu public . Definicje interfejsu można modyfikować wyłącznie poprzez dodanie specyfikatorów dostępu public albo internal . Deklaracje metod wewnątrz definicji interfejsu nie mogą mieć żadnych specyfikatorów dostępu.

W języku ActionScript 3.0 stosowana jest konwencja, zgodnie z którą nazwy interfejsu rozpoczynają się od wielkiej litery I , jednak jako nazwy interfejsu można użyć dowolnego poprawnego identyfikatora. Definicje interfejsów często umieszcza się na najwyższym poziomie pakietu. Definicji interfejsu nie można umieszczać wewnątrz definicji klas ani wewnątrz definicji innych interfejsów.

Interfejs może rozszerzać jeden lub większą liczbę innych interfejsów. Na przykład poniższy interfejs, IExample, rozszerza interfejs IExternalizable:

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

Każda klasa, która implementuje interfejs IExample, musi zawierać implementacje nie tylko metod extra() , lecz także metod writeExternal() i readExternal() odziedziczonych z interfejsu IExternalizable.

Implementowanie interfejsu w klasie

Klasa jest jedynym elementem języka ActionScript 3.0, który może implementować interfejs. Aby zaimplementować jeden lub większą liczbę interfejsów, należy umieścić w deklaracji klasy słowo kluczowe implements . W poniższym przykładzie zdefiniowano dwa interfejsy, IAlpha oraz IBeta , a także klasę Alpha, która implementuje oba te interfejsy:

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 {} 
}

W klasie, która implementuj interfejs, zaimplementowane metody muszą charakteryzować się następującymi cechami:

  • Muszą być zadeklarowane ze specyfikatorem dostępu public .

  • Muszą mieć te same nazwy, co odpowiednie metody w interfejsie.

  • Muszą mieć tę samą liczbę parametrów o tych samych typach, co odpowiednie metody w interfejsie.

  • Muszą zwracać wartości tego samego typu, co odpowiednie metody w interfejsie.

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

Programista ma jednak pewną swobodę w nadawaniu nazw parametrom implementowanych metod. Mimo że liczba parametrów i ich typy danych w zaimplementowanej metodzie muszą być identyczne, jak w metodzie zadeklarowanej w interfejsie, nazwy parametrów nie muszą być identyczne. W poprzednim przykładzie parametr metody Alpha.foo() ma nazwę param :

Jednak w metodzie IAlpha.foo() zadeklarowanej w interfejsie ten sam parametr ma nazwę str :

function foo(str:String):String;

Możliwe jest także swobodne określanie wartości domyślnych parametrów. Definicja interfejsu może zawierać deklaracje funkcji z domyślnymi wartościami parametrów. Metoda implementująca taką deklarację funkcji musi mieć wartość domyślną parametru należącą do tego samego typu danych, co wartość określona w definicji interfejsu, ale wartości te nie muszą być równe. Na przykład w poniższym kodzie zdefiniowano interfejs zawierający metodę z parametrem o wartości domyślnej 3:

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

Poniższa definicja klasy implementuje interfejs Igamma, ale z inną domyślną wartością parametru:

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

Swoboda ta wynika z faktu, że reguły implementacji interfejsów opracowano z myślą o zgodności typów danych, ale wymów identycznych nazw parametrów i domyślnych ich wartości nie jest niezbędny do zapewnienia takiej zgodności.