Klasa jest abstrakcyjną reprezentacją obiektu. W klasie zapisane są informacje o typach danych, jakie obiekt może przechowywać, oraz o możliwych zachowaniach obiektu. Użyteczność takiej abstrakcyjnej reprezentacji nie zawsze jest oczywista, gdy piszemy małe skrypty zawierające zaledwie kilka obiektów wchodzących we wzajemne interakcje. Jednak w miarę rozbudowywania programu wzrasta liczba obiektów, które wymagają zarządzania. W takim przypadku klasy zapewniają lepszą kontrolę nad tworzeniem obiektów oraz nad interakcjami między obiektami.
W języku ActionScript 1.0 programiści mieli do dyspozycji obiekty Function, które pozwalały im na budowanie struktur programistycznych przypominających klasy. W języku ActionScript 2.0 wprowadzono formalną obsługę klas oraz takie słowa kluczowe, jak
class
oraz
extends
. Język ActionScript 3.0 nie tylko obsługuje słowa kluczowe wprowadzone w języku ActionScript 2.0, ale również zapewnia nowe możliwości. Na przykład: język ActionScript 3.0 zawiera udoskonalone funkcje kontroli dostępu w postaci atrybutów
protected
i
internal
. Zapewnia również lepszą kontrolę nad dziedziczeniem dzięki słowom kluczowym
final
i
override
.
Programistom, którzy tworzyli klasy w językach, takich jak Java, C++ lub C#, język ActionScript będzie bardzo przypominał te języki. Język ActionScript wykorzystuje wiele takich samych słów kluczowych i nazw atrybutów, na przykład:
class
,
extends
i
public
.
Uwaga:
W dokumentacji Adobe ActionScript termin właściwość oznacza dowolny element obiektu lub klasy, w tym zmienne, stałe i metody. Ponadto, mimo że terminy „zdefiniowany w klasie” i „statyczny” są często używane zamiennie, tutaj oznaczają dwa różne pojęcia. Na przykład: sformułowanie „właściwości klasy” oznacza wszystkie elementy klasy, a nie tylko jej elementy statyczne.
Definicje klas
Składnia definicji klas w języku ActionScript 3.0 jest podobna do obowiązującej w języku ActionScript 2.0 Prawidłowa definicja klasy składa się ze słowa kluczowego
class
, po którym następuje nazwa klasy. Po nazwie klasy następuje treść klasy, ujęta w nawiasy sześcienne (
{}
). Na przykład poniższy kod tworzy klasę o nazwie Shape zawierającą zmienna o nazwie
visible
:
public class Shape
{
var visible:Boolean = true;
}
Ważna zmianą reguł składni dotyczy definicji klas wewnątrz pakietów. W języku ActionScript 2.0 deklaracja klasy znajdującej się w pakiecie musi zawierać nazwę pakietu. W języku ActionScript 3.0, w którym wprowadzono instrukcję
package
, nazwa pakietu musi znajdować się w deklaracji pakietu, a nie w deklaracji klasy. Poniżej przedstawiono deklaracje klas ilustrujące sposób zdefiniowania klasy BitmapData (wchodzącej w skład pakietu flash.display) w językach ActionScript 2.0 oraz ActionScript 3.0:
// ActionScript 2.0
class flash.display.BitmapData {}
// ActionScript 3.0
package flash.display
{
public class BitmapData {}
}
Atrybuty klas
Język ActionScript 3.0 dopuszcza modyfikowanie definicji klas przy użyciu czterech atrybutów:
Atrybut
|
Definicja
|
dynamic
|
Umożliwia dodawanie właściwości do instancji w czasie wykonywania.
|
final
|
Zakazuje rozszerzania klasy przez inną klasę.
|
internal
(atrybut domyślny)
|
Powoduje, że klasa jest widoczna wewnątrz bieżącego pakietu.
|
public
|
Powoduje, że klasa jest widoczna wszędzie.
|
Aby użyć każdego z tych atrybutów, z wyjątkiem
internal
, należy jawnie przypisać atrybut w definicji. Na przykład brak atrybutu
dynamic
w definicji klasy spowoduje, że w czasie wykonywania nie będzie można dodać właściwości do instancji tej klasy. Atrybut przypisuje się jawnie, umieszczając go na początku definicji klasy, co ilustruje poniższy kod:
dynamic class Shape {}
Niektórzy czytelnicy być może zwrócili uwagę, że na liście brak atrybutu
abstract
. W języku ActionScript 3.0 klasy abstrakcyjne nie są obsługiwane. Ponadto lista nie zawiera atrybutów
private
ani
protected
. Te atrybuty mają znaczenie wyłącznie wewnątrz definicji klasy i nie mogą być stosowane do samych klas. Jeśli klasa nie powinna być widoczna publicznie poza pakietem, należy umieścić ją w pakiecie i oznaczyć atrybutem
internal
. Można również pominąć atrybuty
internal
i
public
, a wówczas kompilator automatycznie dodaje atrybut
internal
. Można również zdefiniować klasę, która będzie widoczna tylko w pliku źródłowym, w którym została zdefiniowana. Należy umieścić taką klasę u dołu pliku źródłowego, poniżej zamykającego nawiasu sześciennego definicji pakietu.
Treść klasy
Treść klasy jest ujęta w nawiasy klamrowe. Definiuje zmienne, stałe oraz metodę klasy. Poniższy przykład przedstawia deklarację klasy Accessibility w języku ActionScript 3.0:
public final class Accessibility
{
public static function get active():Boolean;
public static function updateProperties():void;
}
Wewnątrz treści klasy można także definiować przestrzenie nazw. Poniższy przykład ilustruje definiowanie przestrzeni nazw w treści klasy i wykorzystanie tej przestrzeni jako atrybutu metody klasy:
public class SampleClass
{
public namespace sampleNamespace;
sampleNamespace function doSomething():void;
}
Język ActionScript 3.0 umożliwia uwzględnienie w treści klasy nie tylko definicji, lecz także instrukcji. Instrukcje w treści klasy, ale znajdujące się poza definicją klasy, są wykonywane dokładnie raz. Do wykonania dochodzi wówczas, gdy definicja klasy zostanie napotkana i gdy zostanie utworzony skojarzony obiekt klasy. Poniższy przykład zawiera wywołanie funkcji zewnętrznej
hello()
oraz instrukcję
trace
, która wyświetla komunikat potwierdzający zdefiniowanie klasy:
function hello():String
{
trace("hola");
}
class SampleClass
{
hello();
trace("class created");
}
// output when class is created
hola
class created
W języku ActionScript 3.0 dozwolone jest zdefiniowanie właściwości statycznej i właściwości instancji o tej samej nazwie w treści tej samej klasy. Poniższy przykładowy kod deklaruje zmienną statyczną o nazwie
message
oraz zmienną instancji o tej samej nazwie:
class StaticTest
{
static var message:String = "static variable";
var message:String = "instance variable";
}
// In your script
var myST:StaticTest = new StaticTest();
trace(StaticTest.message); // output: static variable
trace(myST.message); // output: instance variable
Atrybuty właściwości klas
W omówieniu modelu obiektów języka ActionScript termin
właściwość
oznacza każdy element, który może należeć do klasy, w tym zmienne, stałe i metody. Jednak w skorowidzu Adobe ActionScript 3.0 dla platformy Adobe Flash ten termin jest stosowany w węższym zakresie. W tym kontekście właściwość obejmuje tylko elementy klasy, które są zmiennymi lub które są zdefiniowane przez metodę pobierania/ustawiania. W języku ActionScript 3.0 istnieje zbiór atrybutów, które mogą być używane z dowolną właściwością klasy. W następującej tabeli wymieniono te atrybuty:
Atrybut
|
Definicja
|
internal
(atrybut domyślny)
|
Powoduje, że właściwość jest widoczna wewnątrz tego samego pakietu.
|
private
|
Powoduje, że właściwość jest widoczna wewnątrz tej samej klasy.
|
protected
|
Powoduje, że właściwość jest widoczna wewnątrz tej samej klasy i jej klas potomnych.
|
public
|
Powoduje, że właściwość jest widoczna wszędzie.
|
static
|
Określa, że właściwość należy do klasy, a nie do instancji klasy.
|
UserDefinedNamespace
|
Niestandardowa przestrzeń nazw zdefiniowana przez użytkownika
|
Atrybuty przestrzeni nazw sterujące dostępem
Język ActionScript 3.0 udostępnia cztery atrybuty specjalne służące do sterowania dostępem do właściwości zdefiniowanych wewnątrz klasy:
public
,
private
,
protected
i
internal
.
Atrybut
public
powoduje, że właściwość będzie widoczna w każdym miejscu skryptu. Na przykład, aby udostępnić metodę dla kodu poza jej pakietem, należy zadeklarować ją z atrybutem
public
. Zasada ta dotyczy wszystkich właściwości, niezależnie od tego, czy są zadeklarowane za pomocą słowa kluczowego
var
,
const
, czy
function
.
Atrybut
private
powoduje, że właściwość jest widoczna tylko dla kodu wywołującego w klasie definiującej tę właściwość. Atrybut
private
działa zatem inaczej niż w języku ActionScript 2.0, gdzie podklasy miały dostęp do klas prywatnych nadklasy. Inna ważna różnica w działaniu dotyczy dostępu w czasie wykonywania. W języku ActionScript 2.0 słowo kluczowe
private
uniemożliwiało dostęp wyłącznie w trybie kompilacji i dawało się łatwo obejść w czasie wykonywania. W języku ActionScript 3.0 nie jest to już możliwe. Właściwości oznaczone
private
są niedostępne zarówno w czasie kompilacji, jak i w czasie wykonywania.
Na przykład następujący kod tworzy prostą klasę o nazwie PrivateExample, zawierającą jedną zmienną prywatną, a potem próbuje uzyskać dostęp do tej zmiennej prywatnej spoza klasy.
class PrivateExample
{
private var privVar:String = "private variable";
}
var myExample:PrivateExample = new PrivateExample();
trace(myExample.privVar);// compile-time error in strict mode
trace(myExample["privVar"]); // ActionScript 2.0 allows access, but in ActionScript 3.0, this is a run-time error.
W języku ActionScript 3.0 próba dostępu do właściwości prywatnej za pomocą kropki (
myExample.privVar
) powoduje błąd kompilacji w trybie ścisłym. W trybie standardowym błąd zostanie zgłoszony podczas wykonywania, podobnie jak przy próbie użycia operatora dostępu do właściwości (
myExample["privVar"]
).
W poniższej tabeli zamieszczono podsumowanie wyników, jakie da próba dostępu do właściwości prywatnej należącej do klasy zapieczętowanej (niedynamicznej):
|
Tryb ścisły
|
Tryb standardowy
|
kropka (
.
)
|
błąd w czasie kompilacji
|
błąd w czasie wykonywania
|
nawiasy kwadratowe (
[]
)
|
błąd w czasie wykonywania
|
błąd w czasie wykonywania
|
W klasach zadeklarowanych z atrybutem
dynamic
próba dostępu do zmiennej prywatnej nie powoduje błędu w czasie wykonania. Zmienna nie jest widoczna, dlatego zwracana jest wartość
undefined
. Jednak próba użycia kropki w trybie ścisłym spowoduje zgłoszenie błędu w czasie kompilacji. Poniższy przykład jest prawie identyczny z poprzednim, z tym że klasa PrivateExample jest zadeklarowana jako dynamiczna:
dynamic class PrivateExample
{
private var privVar:String = "private variable";
}
var myExample:PrivateExample = new PrivateExample();
trace(myExample.privVar);// compile-time error in strict mode
trace(myExample["privVar"]); // output: undefined
Klasy dynamiczne z reguły zwracają wartość
undefined
, a nie powodują zgłoszenia błędu przy próbie dostępu do właściwości prywatnej z kodu zewnętrznego względem klasy. Zgodnie z poniższą tabelą, błąd jest generowany tylko przy próbie dostępu do właściwości prywatnej za pomocą kropki w trybie ścisłym:
|
Tryb ścisły
|
Tryb standardowy
|
kropka (
.
)
|
błąd w czasie kompilacji
|
undefined
|
nawiasy kwadratowe (
[]
)
|
undefined
|
undefined
|
Atrybut
protected
, będący nowym elementem języka ActionScript 3.0, powoduje, że właściwość jest widoczna dla kodu wywołującego wewnątrz jej własnej klasy lub podklas. Innymi słowy, właściwość z atrybutem protected (chroniona) jest dostępna dla własnej klasy i dla wszystkich klas znajdujących się poniżej w hierarchii dziedziczenia. Zasada ta obowiązuje niezależnie od tego, czy podklasy znajdują się w tym samym, czy w różnych pakietach.
Dla użytkowników, którzy wcześniej korzystali z języka ActionScript 2.0, ta funkcja jest podobna do atrybutu
private
w języku ActionScript 2.0. Atrybut
protected
w języku ActionScript 3.0 jest również podobny do atrybutu
protected
w języku Java. Obydwa atrybuty różnią się jednak, ponieważ wersja Java zapewnia dostęp do modułów wywołujących w tym samym pakiecie. Atrybut
protected
jest użyteczny, jeśli mamy zmienną lub metodę potrzebną w podklasach, ale chcemy ukryć ją przed kodem zewnętrznym względem łańcucha dziedziczenia
Atrybut
internal
, będący nowym elementem języka ActionScript 3.0, powoduje, że właściwość jest widoczna dla kodu wewnątrz jej własnego pakietu. Jest to domyślny atrybut dla kodu w pakiecie i obowiązuje dla każdej właściwości, której jawnie nie przypisano żadnego z poniższych atrybutów:
Atrybut
internal
działa podobnie jak domyślna kontrola dostępu w języku Java, z tym że w języku Java ten poziom dostępu nie ma jawnej nazwy i deklarowany jest po prostu przez pominięcie innych modyfikatorów dostępu. Atrybut
internal
w języku ActionScript 3.0 umożliwia jawne wyrażenie zamiaru uwidocznienia właściwości wyłącznie dla kodu w jej pakiecie.
Atrybut static
Atrybut
static
, którego można używać z właściwościami zadeklarowanymi za pomocą słów kluczowych
var
,
const
lub
function
, umożliwia powiązanie właściwości z klasą, a nie z instancjami tej klasy. Kod zewnętrzny względem klasy odwołuje się do właściwości statycznych za pośrednictwem nazwy klasy, a nie nazwy instancji.
Właściwości statyczne nie są dziedziczone przez podklasy, ale właściwości stanowią część łańcucha zasięgów podklas. Oznacza to, że w treści podklasy można używać zmiennej lub metody statycznej bez odwoływania się do klasy, w której została ona zdefiniowana.
Atrybuty w postaci przestrzeni nazw zdefiniowanych przez użytkownika
Alternatywą dla wstępnie zdefiniowanych atrybutów kontroli dostępu jest utworzenie niestandardowych przestrzeni nazw i korzystanie z nich jak z atrybutów. W każdej definicji można użyć tylko jednego atrybutu w postaci przestrzeni nazw, a ponadto nie można używać przestrzeni nazw razem z którymkolwiek z atrybutów kontroli dostępu: (
public
,
private
,
protected
,
internal
).
Zmienne
Zmienne można deklarować za pomocą słów kluczowych
var
lub
const
. Wartości zmiennych zadeklarowanych za pomocą słowa kluczowego
var
można zmieniać wielokrotnie podczas wykonywania skryptu. Zmienne zadeklarowane za pomocą słowa kluczowego
const
są nazywane
stałymi
i można im przypisać wartość tylko raz. Próba przypisania nowej wartości do zainicjowanej stałej spowoduje błąd.
Zmienne statyczne
Zmienne statyczne deklaruje się za pomocą kombinacji słowa kluczowego
static
oraz instrukcji
var
albo
const
. Zmienne statyczne, powiązane z klasą, a nie z instancją klasy, są użyteczne do przechowywania i współużytkowania informacji należących do całej klasy obiektów. Zmienna statyczna będzie np. dobrym rozwiązaniem, jeśli chcemy rejestrować, ile instancji danej klasy utworzono, lub jeśli chcemy zapisać maksymalną dozwoloną liczbę instancji.
W poniższym przykładzie tworzona jest zmienna
totalCount
, która zlicza utworzone instancje klasy, oraz stała
MAX_NUM
zawierająca maksymalną liczbę instancji. Zmienne
totalCount
i
MAX_NUM
są statyczne, ponieważ zawierają wartości, które mają zastosowanie do klasy jako całości, a nie do konkretnej instancji.
class StaticVars
{
public static var totalCount:int = 0;
public static const MAX_NUM:uint = 16;
}
Kod zewnętrzny względem klasy StaticVars i wszystkich jej podklas może odwoływać się do właściwości
totalCount
i
MAX_NUM
tylko za pośrednictwem samej klasy. Oto przykład poprawnego kodu:
trace(StaticVars.totalCount); // output: 0
trace(StaticVars.MAX_NUM); // output: 16
Nie są dozwolone odwołania do zmiennych statycznych za pośrednictwem instancji klasy, dlatego następujący kod spowoduje zgłoszenie błędów:
var myStaticVars:StaticVars = new StaticVars();
trace(myStaticVars.totalCount); // error
trace(myStaticVars.MAX_NUM); // error
Zmienne zadeklarowane za pomocą słów kluczowych
static
i
const
muszą być inicjowane w deklaracji stałej, co widzimy w deklaracji
MAX_NUM
w przykładowej klasie StaticVars. Nie można przypisać wartości do stałej
MAX_NUM
wewnątrz konstruktora lub metody instancji. Poniższy kod powoduje zgłoszenie błędu, ponieważ zastosowano w nim niepoprawny sposób inicjowania stałej statycznej:
// !! Error to initialize static constant this way
class StaticVars2
{
public static const UNIQUESORT:uint;
function initializeStatic():void
{
UNIQUESORT = 16;
}
}
Zmienne instancji
Do zmiennych instancji zalicza się właściwości zadeklarowane za pomocą słów kluczowych
var
i
const
, ale bez słowa kluczowego
static
. Zmienne instancji, powiązane z instancjami klasy, a nie z całą klasą, są użyteczne do przechowywania wartości specyficznych dla instancji. Na przykład klasa Array ma właściwość instancji o nazwie
length
, która przechowuje liczbę elementów tablicy konkretnej instancji klasy Array.
Niedozwolone jest przesłanianie zmiennych instancji w podklasie, niezależnie od tego, czy zostały zadeklarowane jako
var
, czy jako
const
. Efekt zbliżony do przesłonięcia zmiennej można jednak uzyskać, przesłaniając metody pobierające i ustawiające.
Metody
Metody są funkcjami stanowiącymi część definicji klasy. Z chwilą utworzenia instancji klasy metoda zostaje powiązana z tą instancją. W przeciwieństwie do funkcji zadeklarowanych poza klasą, metody nie można używać w oderwaniu od instancji, z którą jest powiązania.
Do definiowania metod służy słowo kluczowe
function
. Podobnie, jak w przypadku każdej właściwości klasy, do metod można zastosować dowolne atrybuty właściwości klas, między innymi private, protected, public, internal, static lub custom namespace. Można również użyć instrukcji funkcji, takiej jak poniższa:
public function sampleFunction():String {}
Można też użyć zmiennej, której przypisane zostanie wyrażenie funkcyjne, w następujący sposób:
public var sampleFunction:Function = function () {}
W większości przypadków należy użyć instrukcji funkcyjnej, a nie wyrażenia, co wynika z następujących powodów:
-
Instrukcje funkcyjne są bardziej zwięzłe i czytelne.
-
Instrukcje funkcyjne umożliwiają stosowanie słów kluczowych
override
i
final
.
-
Instrukcje funkcyjne tworzą silniejsze powiązanie między identyfikatorem — tj. nazwą funkcji — a kodem w treści metody. Ponieważ wartość zmiennej można zmienić za pomocą instrukcji przypisania, połączenie między zmienną a jej wyrażeniem funkcyjnym może zostać w dowolnej chwili zerwane. Mimo że istnieje obejście tego problemu, polegające na zadeklarowaniu zmiennej za pomocą słowa
const
, a nie
var
, takie postępowanie nie należy do dobrej praktyki programistycznych, ponieważ utrudnia zrozumienie kodu i uniemożliwia stosowanie słów kluczowych
override
i
final
.
Do sytuacji, w których konieczne jest użycie wyrażenia funkcyjnego, należy powiązanie funkcji z obiektem prototypowym.
Konstruktory
Konstruktory
(nazywane też metodami-konstruktorami) to funkcje o tej samej nazwie, co klasa, w której są zdefiniowane. Kod zawarty w konstruktorze jest wykonywany za każdym razem, gdy za pomocą słowa kluczowego
new
tworzona jest nowa instancja klasy. Na przykład w poniższym kodzie zdefiniowano prostą klasę o nazwie Example, która zawiera jedną właściwość o nazwie
status
. Wartość początkowa zmiennej
status
jest przypisywana wewnątrz konstruktora.
class Example
{
public var status:String;
public function Example()
{
status = "initialized";
}
}
var myExample:Example = new Example();
trace(myExample.status); // output: initialized
Konstruktory mogą być tylko publiczne, ale użycie atrybutu
public
jest opcjonalne. W odniesieniu do konstruktora nie można używać żadnych innych specyfikatorów dostępu, w tym
private
,
protected
lub
internal
. Konstruktora nie można również zadeklarować w przestrzeni nazw zdefiniowanej przez użytkownika.
Konstruktor może jawnie wywołać konstruktor swojej bezpośredniej nadklasy. Służy do tego instrukcja
super()
. Jeśli konstruktor nadklasy nie zostanie wywołany jawnie, kompilator automatycznie wstawi wywołanie przed pierwszą instrukcją w treści konstruktora. Możliwa jest również wywoływanie metod nadklasy poprzez dodanie przedrostka
super
jako odwołania do nadklasy. Jeśli w tym samym konstruktorze ma być używana zarówno instrukcja
super()
, jak i przedrostek
super
, to instrukcja
super()
musi występować wcześniej. W przeciwnym razie odwołanie przez przedrostek
super
nie będzie działać zgodnie z oczekiwaniami. Konstruktor
super()
należy także wywołać przed ewentualną instrukcją
throw
lub
return
.
Poniższy przykład ilustruje skutki użycia odwołania
super
przed wywołaniem konstruktora
super()
. Nowa klasa, ExampleEx, rozszerza klasę Example. Konstruktor ExampleEx próbuje uzyskać dostęp do zmiennej status zdefiniowanej w jego nadklasie, ale próba ta ma miejsce przed wywołaniem
super()
. Instrukcja
trace()
w konstruktorze ExampleEx wyświetla wartość
null
, ponieważ zmienna
status
nie jest dostępna przed wykonaniem konstruktora
super()
.
class ExampleEx extends Example
{
public function ExampleEx()
{
trace(super.status);
super();
}
}
var mySample:ExampleEx = new ExampleEx(); // output: null
Mimo że formalnie dozwolone jest użycie instrukcji
return
w konstruktorze, nie jest dozwolone zwrócenie wartości. Innymi słowy, instrukcja
return
nie może być skojarzona z wyrażeniem lub wartością. W związku z tym konstruktory nie mogą zwracać wartości i nie mogą mieć określonego typu zwracanej wartości.
Jeśli w klasie nie zostanie zdefiniowany konstruktor, kompilator automatycznie utworzy pustą metodę-konstruktor. Jeśli klasa rozszerza inną klasę, kompilator doda wywołanie
super()
do wygenerowanego konstruktora.
metody statyczne
Metody statyczne, nazywane także
metodami klas
, to metody zadeklarowane ze słowem kluczowym
static
. Metody statyczne, które są powiązane z klasą, a nie z instancją klasy, są przydatne do enkapsulacji funkcjonalności wpływającej na coś innego niż stan konkretnej instancji. Ponieważ metody statyczne są powiązane z klasą jako całością, dostęp do nich jest możliwy tylko za pośrednictwem klasy, a nie jej instancji.
Metody statyczne są przydatne do enkapsulacji funkcjonalności, która nie ogranicza się do zmiany stanu instancji klasy. Innymi słowy, metoda powinna być statyczna, jeśli jej działanie nie wpływa bezpośrednio na wartość instancji klasy. Na przykład klasa Date ma metodę statyczną o nazwie
parse()
, która konwertuje ciąg znaków na liczbę. Metoda jest statyczna, ponieważ nie wpływa na poszczególne instancje klasy. Metoda
parse()
analizuje ciąg znaków reprezentujący wartość daty i zwraca liczbę zgodną z wewnętrzną reprezentacją obiektu Date. Ta metoda nie jest metodą instancji, ponieważ nie ma sensu stosowanie jej do instancji klasy Date.
Porównajmy teraz metodę statyczną
parse()
z jedną z metod instancji klasy Date, np. z metodą
getMonth()
.
getMonth()
to metoda instancji, ponieważ operuje bezpośrednio na wartości instancji, pobierając konkretny składnik — miesiąc — instancji Date.
Ponieważ metody statyczne nie są powiązane z poszczególnymi instancjami, nie jest dozwolone użycie słów kluczowych
this
lub
super
w treści metody statycznej. Zarówno odwołanie
this
, jak i odwołanie
super
, mają znaczenie tylko w kontekście metody instancji.
Inaczej niż w niektórych innych językach programowania opartych na koncepcji klas, metody statyczne w języku ActionScript 3.0 nie są dziedziczone.
Metody instancji
Metody instancji są to metody zadeklarowane bez słowa kluczowego
static
. Metody instancji, które są powiązane z instancjami klasy, a nie z klasą jako całością, są użyteczne przy implementowaniu funkcjonalności wpływającej na poszczególne instancje klasy. Na przykład klasa Array zawiera metodę instancji o nazwie
sort()
, która operuje bezpośrednio na instancjach klasy Array.
Zarówno zmienne statyczne, jak i zmienne instancji znajdują się w zasięgu treści metody instancji, co oznacza, że do zmiennych zdefiniowanych w tej samej klasie można odwoływać się za pomocą prostych identyfikatorów. Na przykład poniższa klasa, CustomArray, rozszerza klasę Array. Klasa CustomArray definiuje zmienną statyczną o nazwie
arrayCountTotal
, która rejestruje łączną liczbę instancji klasy, zmienną instancji o nazwie
arrayNumber
, która rejestruje kolejność tworzenia instancji, oraz metodę instancji o nazwie
getPosition()
, która zwraca wartości tych zmiennych.
public class CustomArray extends Array
{
public static var arrayCountTotal:int = 0;
public var arrayNumber:int;
public function CustomArray()
{
arrayNumber = ++arrayCountTotal;
}
public function getArrayPosition():String
{
return ("Array " + arrayNumber + " of " + arrayCountTotal);
}
}
Mimo że kod zewnętrzny względem klasy musi mieć dostęp do zmiennej statycznej
arrayCountTotal
za pośrednictwem obiektu klasy, w formie
CustomArray.arrayCountTotal
, kod rezydujący w treści metody
getPosition()
może odwoływać się bezpośrednio do zmiennej statycznej
arrayCountTotal
. Zasada ta obowiązuje nawet dla zmiennych statycznych w nadklasach. Chociaż w języku ActionScript 3.0 właściwości statyczne nie są dziedziczone, właściwości statyczne z nadklas należą do aktualnego zasięgu. Na przykład klasa Array zawiera kilka zmiennych statycznych, z których jedna jest stałą o nazwie
DESCENDING
. Kod rezydujący w podklasie Array może mieć dostęp do statycznej stałej
DESCENDING
za pomocą prostego identyfikatora:
public class CustomArray extends Array
{
public function testStatic():void
{
trace(DESCENDING); // output: 2
}
}
Wartość odwołania
this
w treści metody instancji jest odwołaniem do instancji, z którą ta metoda jest powiązana. Poniższy kod demonstruje, że odwołanie
this
wskazuje na instancję zawierającą metodę:
class ThisTest
{
function thisValue():ThisTest
{
return this;
}
}
var myTest:ThisTest = new ThisTest();
trace(myTest.thisValue() == myTest); // output: true
Dziedziczeniem metody instancji można sterować za pomocą słów kluczowych
override
i
final
. Atrybut
override
umożliwia przedefiniowanie metody odziedziczonej, a atrybut
final
zapobiega przesłanianiu metody przez podklasy.
Akcesory pobierające i ustawiające
Funkcje-akcesory pobierające i ustawiające, nazywane także
metodami pobierającymi
i
metodami ustawiającymi
, umożliwiają realizację koncepcji ukrywania i enkapsulacji informacji, a jednocześnie stworzenie wygodnego interfejsu programistycznego dla samodzielnie tworzonych klas. Funkcje pobierające i ustawiające pozwalają na zachowanie prywatności właściwości klasy, ale jednocześnie umożliwiają użytkownikom klasy dostęp do tych właściwości w taki sposób, jak do zmiennych, a nie w formie wywołania metody klasy.
Zaletą takiej strategii jest możliwość uniknięcia tradycyjnych akcesorów o „nieprzyjaznych” nazwach, takich jak
getPropertyName()
i
setPropertyName()
. Inną zaletą funkcje pobierających i ustawiających jest możliwość obycia się bez pary funkcji publicznych przypadającej na każdą właściwość, która ma być dostępna i do odczytu, i do zapisu.
Poniższa klasa przykładowa o nazwie GetSet zawiera funkcje pobierającą i ustawiającą o nazwie
publicAccess()
, która umożliwia dostęp do zmiennej prywatnej o nazwie
privateProperty
:
class GetSet
{
private var privateProperty:String;
public function get publicAccess():String
{
return privateProperty;
}
public function set publicAccess(setValue:String):void
{
privateProperty = setValue;
}
}
Próba bezpośredniego dostępu do właściwości
privateProperty
powoduje zgłoszenie błędu:
var myGetSet:GetSet = new GetSet();
trace(myGetSet.privateProperty); // error occurs
Użytkownik klasy GetSet ma natomiast do dyspozycji mechanizm, który z pozoru przypomina właściwość o nazwie
publicAccess
, ale faktycznie jest parą akcesorów — złożoną z funkcji pobierającej i ustawiającej — operujących na właściwości prywatnej
privateProperty
. W poniższym przykładzie tworzona jest instancja klasy GetSet, a następnie wartość właściwości
privateProperty
ustawiana jest za pomocą publicznego akcesora o nazwie
publicAccess
:
var myGetSet:GetSet = new GetSet();
trace(myGetSet.publicAccess); // output: null
myGetSet.publicAccess = "hello";
trace(myGetSet.publicAccess); // output: hello
Funkcje pobierające i ustawiające umożliwiają także przesłanianie właściwości dziedziczonych z nadklasy, co nie jest możliwe w przypadku zwykłych zmiennych należących do klasy. Zmienne należące do klasy zadeklarowane za pomocą słowa kluczowego
var
nie mogą być przesłaniane w podklasach. Jednak ograniczenie to nie obowiązuje w przypadku właściwości tworzonych za pomocą funkcji pobierających i ustawiających. W odniesieniu do funkcji pobierających i ustawiających odziedziczonych z nadklasy można używać atrybutu
override
.
Metody powiązane
Metoda powiązana, nazywana czasem
zamknięciem metody
, to po prostu metoda wyodrębniona z jej instancji. Przykładami metod powiązanych są metody przekazywane jako argumenty do funkcji lub zwracane jako wartości z funkcji. Metody powiązane, będące nowym elementem języka ActionScript 3.0, są podobne do zamknięć funkcji w tym, że zachowują swoje środowisko leksykalne nawet po wyodrębnieniu z instancji. Zasadnicza różnica między metodą powiązaną a zamknięciem funkcji polega na tym, że odwołanie
this
w metodzie powiązanej pozostaje powiązane z instancją implementującą tą metodę. Innymi słowy, odwołanie
this
w metodzie powiązanej zawsze wskazuje na oryginalny obiekt, który zaimplementował metodę. W przypadku zamknięć funkcji odwołanie
this
ma charakter ogólny, czyli wskazuje na obiekt lub funkcję, z którym jest skojarzone w momencie wywołania.
Zrozumienie koncepcji metod powiązanych jest ważne w przypadku posługiwania się słowem kluczowym
this
. Jak pamiętamy, słowo kluczowe
this
jest odwołaniem do obiektu nadrzędnego metody. Większość programistów używających języka ActionScript oczekuje, że słowo kluczowe
this
przedstawia obiekt lub klasę zawierającą definicję metody. Jednak bez mechanizm wiązania metod warunek ten nie zawsze byłby spełniony. Na przykład we wcześniejszych wersjach języka ActionScript odwołanie
this
nie zawsze wskazywało na instancję, która implementowała metodę. W języku ActionScript 2.0 w metodach wyodrębnionych z instancji nie tylko słowo kluczowe
this
nie było powiązane z oryginalną instancją, lecz również nie były dostępne zmienne i metody należące do klasy instancji. Nie stanowi to problemu w języku ActionScript 3.0, w którym metody powiązane są tworzone automatycznie podczas przekazywania metod jako parametrów. Metody powiązane gwarantują, że słowo kluczowe
this
zawsze odwołuje się do obiektu lub klasy, w którym metoda jest zdefiniowana.
Poniższy kod definiuje klasę o nazwie ThisTest, która zawiera metodę o nazwie
foo()
definiującą metodę powiązaną, a także metodę o nazwie
bar()
, która zwraca metodę powiązaną. Kod zewnętrzny względem klasy tworzy instancję klasy ThisTest, wywołuje metodę
bar()
i zapisuje zwróconą wartość w zmiennej o nazwie
myFunc
.
class ThisTest
{
private var num:Number = 3;
function foo():void // bound method defined
{
trace("foo's this: " + this);
trace("num: " + num);
}
function bar():Function
{
return foo; // bound method returned
}
}
var myTest:ThisTest = new ThisTest();
var myFunc:Function = myTest.bar();
trace(this); // output: [object global]
myFunc();
/* output:
foo's this: [object ThisTest]
output: num: 3 */
Ostatnie dwa wiersze kodu demonstrują fakt, że odwołanie
this
w metodzie powiązanej
foo()
nadal wskazuje na klasę ThisTest, mimo że odwołanie
this
w wierszy bezpośrednio poprzedzającym wskazuje na obiekt globalny. Co więcej, metoda powiązana przechowywana w zmiennej
myFunc
wciąż ma dostęp do zmiennych klasy ThisTest. Gdyby ten sam kod został uruchomiony w języku ActionScript 2.0, odwołania
this
byłyby identyczne, a zmienna
num
miałaby wartość
undefined
.
Jednym z obszarów, w których nowa koncepcja metod powiązanych jest najbardziej zauważalna, są podprogramy obsługi zdarzeń, ponieważ metoda
addEventListener()
wymaga, aby jako argument przekazać do niej funkcję lub metodę.
Wyliczenia w oparciu o klasy
Wyliczenia
to niestandardowe typy danych, które definiuje się w celu enkapsulacji małego zbioru wartości. W języku ActionScript 3.0 nie istnieje specjalny mechanizm wyliczeń, porównywalny ze słowem kluczowym
enum
w języku C++ lub interfejsem Enumeration w języku Java. Możliwe jest jednak tworzenie wyliczeń przy użyciu klas i stałych statycznych. Na przykład: klasa PrintJob w języku ActionScript 3.0 korzysta z wyliczenia o nazwie PrintJobOrientation zawierającego wartości:
"landscape"
i
"portrait"
, co ilustruje poniższy kod:
public final class PrintJobOrientation
{
public static const LANDSCAPE:String = "landscape";
public static const PORTRAIT:String = "portrait";
}
Przyjęto konwencję, że klasa wyliczeniowa powinna być zadeklarowana z atrybutem
final
, ponieważ nie ma potrzeby rozszerzania klasy. Klasa zawiera tylko elementy statyczne, co oznacza, że użytkownik nie tworzy instancji klasy. Dostęp do wartości wyliczenia odbywa się wprost przez obiekt klasy, co ilustruje poniższy fragment kodu:
var pj:PrintJob = new PrintJob();
if(pj.start())
{
if (pj.orientation == PrintJobOrientation.PORTRAIT)
{
...
}
...
}
Wszystkie klasy wyliczeniowe języka ActionScript 3.0 zawierają tylko zmienne typu String, int lub uint. Korzyścią ze stosowania wyliczeń w miejsce literalnych ciągów lub wartości liczbowych jest możliwość łatwiejszego znajdowania błędów typograficznych. W wypadku pomyłki w zapisie wartości wyliczeniowej kompilator języka ActionScript zgłosi błąd. Gdy natomiast używane są literały, kompilator nie sygnalizuje obecności nieprawidłowo wpisanego słowa lub błędnej liczby. W poprzednim przykładzie kompilator generuje błąd, jeśli nazwa stałej wyliczeniowej jest nieprawidłowa, co ilustruje następujący fragment:
if (pj.orientation == PrintJobOrientation.PORTRAI) // compiler error
Jednak kompilator nie zgłosi błędu w razie pomyłki w zapisie literalnego ciągu znaków:
if (pj.orientation == "portrai") // no compiler error
Druga technika tworzenia wyliczeń również wymaga utworzenia osobnej klasy ze statycznymi właściwościami dla wartości wyliczeniowych. Jednak różni się od pierwszej tym, że każda z właściwości statycznych zawiera instancję klasy, a nie ciąg znaków lub wartość całkowitą. Poniższy przykładowy kod tworzy klasę wyliczeniową zawierającą dni tygodnia:
public final class Day
{
public static const MONDAY:Day = new Day();
public static const TUESDAY:Day = new Day();
public static const WEDNESDAY:Day = new Day();
public static const THURSDAY:Day = new Day();
public static const FRIDAY:Day = new Day();
public static const SATURDAY:Day = new Day();
public static const SUNDAY:Day = new Day();
}
Ta technika nie jest stosowana w języku ActionScript 3.0, ale wielu programistów korzysta z niej z uwagi na ściślejszą kontrolę typów. Na przykład metoda, która zwraca wartość wyliczeniową, może ograniczyć zbiór wartości zwracanych do konkretnego typu wyliczeniowego. Poniższy kod ilustruje nie tylko funkcję zwracającą dzień tygodnia, lecz również wywołanie funkcji, w której typ wyliczeniowy został użyty jako wskazany typ zadeklarowany:
function getDay():Day
{
var date:Date = new Date();
var retDay:Day;
switch (date.day)
{
case 0:
retDay = Day.MONDAY;
break;
case 1:
retDay = Day.TUESDAY;
break;
case 2:
retDay = Day.WEDNESDAY;
break;
case 3:
retDay = Day.THURSDAY;
break;
case 4:
retDay = Day.FRIDAY;
break;
case 5:
retDay = Day.SATURDAY;
break;
case 6:
retDay = Day.SUNDAY;
break;
}
return retDay;
}
var dayOfWeek:Day = getDay();
Możemy udoskonalić klasę Day w taki sposób, aby kojarzyła liczbę całkowitą z każdym dniem tygodnia i udostępniała metodę
toString()
zwracającą ciąg znaków z nazwą dnia.
Klasy zasobów osadzonych
W języku ActionScript 3.0 istnieją specjalne klasy, nazywane
klasami zasobów osadzonych
, służące do reprezentacji zasobów osadzonych.
Zasób osadzony
to zasób, taki jak dźwięk, obraz lub czcionka, włączony do pliku SWF w czasie kompilacji. Osadzenie zasobu, jako alternatywa dla dynamicznego ładowania go, gwarantuje dostępność zasobu w czasie wykonywania, jednak kosztem większej objętości pliku SWF.
Korzystanie z klas zasobów osadzonych w środowisku Flash Professional
Aby osadzić zasób, należy najpierw umieścić go w bibliotece pliku FLA. Następnie, korzystając z właściwości połączenia zasobu, należy podać nazwę klasy zasobu osadzonego. Jeśli klasy o tej nazwie nie ma w ścieżce klas, to zostanie automatycznie wygenerowana. Następnie tworzymy instancję klasy zasobu osadzonego i korzystamy z właściwości i metod zdefiniowanych lub odziedziczonych przez tę klasę. Poniższy przykładowy kod umożliwia odtworzenie osadzonego dźwięku połączonego z klasą zasobu osadzonego o nazwie PianoMusic:
var piano:PianoMusic = new PianoMusic();
var sndChannel:SoundChannel = piano.play();
Można również wykorzystać znacznik metadanych
[Embed]
w celu osadzenia zasobów w projekcie Flash Professional, co zostało opisane w dalszej części niniejszego podręcznika. Jeśli w kodzie używany jest znacznik
[Embed]
, program Flash Professional wykorzystuje kompilator Flex w celu skompilowania projektu, nie korzysta natomiast z kompilatora Flash Professional.
Korzystanie z osadzonych klas zasobów, które wykorzystują kompilator Flex
W przypadku kompilowania kodu za pomocą kompilatora Flex w celu osadzenia zasobu w kodzie ActionScript należy użyć znacznika metadanych
[Embed]
. Zasób należy umieścić w głównym folderze źródłowym lub innym folderze w ścieżce budowania projektu. Gdy tylko kompilator Flex napotyka znacznik Embed, tworzy klasę zasobu osadzonego. Dostęp do tej klasy jest możliwy za pośrednictwem zmiennej o typie danych Class, którą deklaruje się bezpośrednio po znaczniku metadanych
[Embed]
. W poniższym przykładowym kodzie osadzony jest dźwięk o nazwie sound1.mp3, a odwołanie do klasy zasobu osadzonego powiązanej z tym dźwiękiem jest przechowywane w zmiennej o nazwie
soundCls
. Następnie przykładowy kod tworzy instancję klasy obiektu osadzonego i wywołuje metodę
play()
tej instancji:
package
{
import flash.display.Sprite;
import flash.media.SoundChannel;
import mx.core.SoundAsset;
public class SoundAssetExample extends Sprite
{
[Embed(source="sound1.mp3")]
public var soundCls:Class;
public function SoundAssetExample()
{
var mySound:SoundAsset = new soundCls() as SoundAsset;
var sndChannel:SoundChannel = mySound.play();
}
}
}
Adobe Flash Builder
Aby użyć znacznika metadanych
[Embed]
w projekcie ActionScript w środowisku Flash Builder, należy zaimportować wszelkie potrzebne klasy ze środowiska Flex. Na przykład, aby osadzać dźwięki, należy zaimportować klasę mx.core.SoundAsset. Aby użyć środowiska Flex, należy umieścić plik framework.swc w ścieżce budowania projektu ActionScript. Spowoduje to zwiększenie wielkości pliku SWF.
Adobe Flex
We Flex alternatywny sposób osadzania zasobów polega na użyciu dyrektywy
@Embed()
w definicji znacznika MXML.
|
|
|