Könyvhöz tartozó online melléklet

Könyv: ActionScript 3.0 a gyakorlatban (Colin Moock)

Melléklet: Olvasson bele!

4

4. Statikus változók és tagfüggvények

Az 1. fejezetben megtanultuk, miként határozhatjuk meg objektumunk jellemzőit és viselkedését a példányváltozókkal és -tagfüggvényekkel. Most azzal foglalkozunk, hogyan kezelhetjük azokat az adatokat és műveleteket, amelyek közvetlenül az osztályhoz, nem pedig annak példányaihoz kapcsolódnak.

Statikus változók

Az előző fejezetekben komoly tapasztalatokra tettünk szert a példányváltozók használata terén, amelyek az osztályok egyes példányaihoz kapcsolódnak. A statikus változók ugyanakkor magához az osztályhoz tartoznak, nem pedig annak egy példányához – olyan adatokat jelenítenek meg, amelyek logikailag a teljes osztályhoz kötődnek, tehát nem változnak az egyes példányok esetében. Így például egy párbeszédablakokat megjelenítő osztály alkalmazhat statikus változókat a párbeszédablakok új példányainak alapértelmezett méretezéséhez, egy autóversenyprogramban pedig az autókat megjelenítő osztály egy statikus változóban rögzítheti az elérhető legnagyobb sebességet, amely így minden részt vevő autóra érvényes lesz.

A példányváltozókhoz hasonlóan statikus társaikat is az osztályon belüli változómeghatározással kell létrehoznunk, de ezúttal fel kell tüntetnünk a static jellemzőt, az alábbiak szerint:

class ValamilyenOsztály {

static var azonosító = érték;

}

A példányváltozókhoz hasonlóan itt is alkalmazhatunk elérésmódosítókat, ráadásul ugyanaz a négy típus – public, internal, protected és private – áll a rendelkezésünkre. Ha nem adunk meg semmilyen módosítót, az ActionScript csomagszintű elérést (internal) alkalmaz. A módosítót jellemzően a static kulcsszó elé helyezik, mint az az alábbi kódban is látható:

class ValamilyenOsztály {

private static var azonosító = érték;

}

A statikus változók eléréséhez nevük elé az osztály nevét, majd egy pontot kell biggyesztenünk, valahogy így:

ValamilyenOsztály.azonosító = érték;

A változót meghatározó osztályban az azonosító önmagában is használható (az osztálynév és a pont nélkül). Így például egy A nevű osztály v statikus változója esetében az osztályon belül az A.v és a v kifejezések azonosak. Mindazonáltal a statikus változók jobb megkülönböztetése érdekében számos fejlesztő (és könyvünk is) kiírja az osztálynevet, még akkor is, ha erre nincs feltétlenül szükség.

Egyazon osztályban létezhet ugyanolyan nevű statikus és példányváltozó. Tekintsük A nevű osztályunkat, és tegyük fel, hogy létrehoztunk benne egy v nevű statikus, valamint egy azonos nevű példányváltozót. Ilyenkor a v önmagában a példányváltozóra hivatkozik, míg a statikus változót az osztálynévvel kiegészített alakkal (A.v) érhetjük el. Azt mondjuk, hogy a példányváltozó elfedi a statikus változót.

Hozzunk létre most pár statikus változót a VirtualPet osztályunkban. Amint az előzőekben megtanulhattuk, a statikus változók olyan adatokat követnek nyomon, amelyek logikailag a teljes osztályhoz tartoznak, nem pedig annak egyes példányaihoz. Ilyen adatunk pedig már kettő is van: a kisállat nevének maximális hossza, valamint az a kalóriamennyiség, amelyet elfogyaszthat. Ezeket az adatokat a maxNameLength, valamint a maxCalories nevű új statikus változókban tároljuk. Az osztályon kívül nem lesz rájuk szükség, így a private módosítóval határozzuk meg őket, az alábbiak szerint (a VirtualPet osztály kódjának többi részét helytakarékosság miatt most elhagyjuk):

package zoo {

internal class VirtualPet {

private static var maxNameLength = 20;

private static var maxCalories = 2000;

 

// A kód többi részét most elhagyjuk...

}

}

A két új statikus változóval módosíthatjuk a getHunger(), az eat() és a setName() tagfüggvényünk kódját. Az eredményt (a VirtualPet osztály teljes kódját) a 4.1. példában szemlélhetjük meg. Érdemes megjegyeznünk, hogy a statikus változókat a közmegegyezés szerint a példányváltozók előtt soroltuk fel.

4.1. példa A VirtualPet osztály

package zoo {

internal class VirtualPet {

private static var maxNameLength = 20;

private static var maxCalories = 2000;

private var petName;

// Kisállatainknak a maximális kalóriamennyiség felét adjuk induláskor.

private var currentCalories = VirtualPet.maxCalories/2;

 

public function VirtualPet (name) {

setName(name);

}

 

public function eat (numberOfCalories) {

var newCurrentCalories = currentCalories + numberOfCalories;

if (newCurrentCalories > VirtualPet.maxCalories) {

currentCalories = VirtualPet.maxCalories;

} else {

currentCalories = newCurrentCalories;

}

}

 

public function getHunger ( ) {

return currentCalories / VirtualPet.maxCalories;

}

 

public function setName (newName) {

// Ha az új név hosszabb maxNameLength karakternél...

if (newName.length > VirtualPet.maxNameLength) {

// ...csonkoljuk

newName = newName.substr(0, VirtualPet.maxNameLength);

} else if (newName == "") {

// ...egyébként, ha az új név üres karakterlánc, a tagfüggvény futása

// véget ér, és a petName eredeti értéke nem változik

return;

}

 

// Rendeljük hozzá az új, ellenőrzött nevet a petName változóhoz

petName = newName;

}

 

public function getName ( ) {

return petName;

}

}

}

Figyeljük meg, miként segít a maxNameLength és a maxCalories változó a kód központosításában. A korábbiakban, ha például a név hosszának korlátját szerettük volna megváltoztatni, két helyen is bele kellett nyúlnunk a setName() tagfüggvény kódjába – ez a folyamat pedig időrabló, és egyúttal magában hordozza a hiba lehetőségét. Osztályunk új kódjában mindehhez csak a maxNameLength értékét kell átírnunk, és a módosítást mindenütt érvényesítjük.

A magyarázat nélküli literálértékeket – mint a setName() előző változatában a 20 – „mágikus értékeknek” szokták hívni, hiszen fontos szerepet töltenek be a programban, viszont hogy mi is ez a szerep, az korántsem magától értetődő. Ezért kerüljük az ilyen „mágikus értékek” használatát – a statikus változók hadrendbe állításával legtöbbször célt érhetünk.

A statikus változókat gyakran olyan beállítások tárolására használják, amelyek a program indulása után nem változnak. Ha ezt a korlátozást rögzíteni is szeretnénk, változónkat állandóként határozhatjuk meg – erről szólunk a következőkben.

Állandók

Az állandók olyan statikus, helyi vagy példányváltozók, amelyek értéke a kezdeti értékadást követően nem változik. Létrehozásukhoz a változók meghatározásának hagyományos alakját használjuk, de a var helyett a const kulcsszóval. A hagyomány szerint az állandók nevét csupa nagybetűvel írjuk. Lássuk most, hogyan is határozhatunk meg egy statikus állandót osztályunk törzsében:

static const AZONOSÍTÓ = érték

Ha egy példányváltozót szeretnénk állandóként meghatározni, az alábbi sort kell elhelyeznünk az osztály törzsében:

const AZONOSÍTÓ = érték

Ha pedig egy helyi változót szeretnénk állandóként alkalmazni, a következőket írhatjuk egy tagfüggvény vagy függvény törzsébe:

const AZONOSÍTÓ = érték

Az előző három példában az AZONOSÍTÓ az állandó nevét, míg az érték a kezdeti értékét jelöli. A statikus és a helyi állandók esetében ezt az értéket többé nem lehet megváltoztatni.

A példányváltozóknál a helyzet kissé bonyolultabb. Ha szigorú módban fordítjuk a programot, az érték hozzárendelése után az állandók nem módosíthatók. Ha azonban egyszerű módban fordítjuk a programot, a változó meghatározását tartalmazó osztály konstruktorában még módosíthatjuk az értékét, másutt azonban nem. (A szigorú és az egyszerű fordítási mód közti különbségekről a 7. fejezetben olvashatunk.)

Az állandókat leggyakrabban statikus változók rögzített értékeinek megjelenítésére használják, amelyek egy beállítás különböző állapotait adják meg. Tegyük fel, hogy egy ébresztőóra-programot készítünk, ami naponta bekapcsol a megadott időpontban. Három működési módja van: a vizuális (ilyenkor egy ikon villog a képernyőn), a hangos (csörgés), valamint az egyszerre vizuális és hangos. Ébresztőóránkat az AlarmClock nevű osztály működteti. A három ébresztési mód megjelenítésére három állandó statikus változót – MODE_VISUAL, MODE_AUDIO és MODE_BOTH – határozunk meg. Mindhárom állandóhoz egy-egy számértéket rendelünk: az 1-es felel meg a vizuális módnak, a 2-es a hangos módnak, a 3-as pedig a vizuális és hangos módnak. Az állandók meghatározását az alábbiakban láthatjuk:

public class AlarmClock {

public static const MODE_VISUAL = 1;

public static const MODE_AUDIO = 2;

public static const MODE_BOTH = 3;

}

Az AlarmClock objektumok aktuális működési módjának nyomon követésére szolgál a mode nevű példányváltozó – ehhez rendelhetjük hozzá a megfelelő értékeket (1, 2 vagy 3). Az alábbiakban új AlarmClock objektumainkat hangos ébresztésre állítjuk be (2-es mód):

public class AlarmClock {

public static const MODE_VISUAL = 1;

public static const MODE_AUDIO = 2;

public static const MODE_BOTH = 3;

 

private var mode = AlarmClock.MODE_AUDIO;

}

Amikor eljön az ébresztés ideje, az AlarmClock objektum a működési mód alapján dönti el, hogyan viselkedjen. Az alábbiakban ezt a döntéshozatali folyamatot láthatjuk a korábban megadott állandókkal:

public class AlarmClock {

public static const MODE_VISUAL = 1;

public static const MODE_AUDIO = 2;

public static const MODE_BOTH = 3;

 

private var mode = AlarmClock.MODE_AUDIO;

 

private function signalAlarm ( ) {

if (mode == MODE_VISUAL) {

// A villogó ikon megjelenítése

} else if (mode == MODE_AUDIO) {

// A hang lejátszása

} else if (mode == MODE_BOTH) {

// Az ikon megjelenítése és a hang lejátszása

}

}

}

Vegyük észre, hogy ebben a kódban valójában nincs is szükség a működési módokat jelző állandókra. Ugyanezt a folyamatot véghez vihettük volna literálokkal, vagyis mágikus értékekkel is. Az állandók szerepe itt mindössze annyi, hogy érthetőbbé teszik a számértékek szerepét. Az összehasonlítás kedvéért bemutatjuk az AlarmClock osztály kódját az állandók nélkül. Nyilvánvaló, hogy a megjegyzések elolvasása nélkül meglehetősen nehéz kitalálni, mit is takarnak az egyes számértékek.

public class AlarmClock {

private var mode = 2;

 

private function signalAlarm ( ) {

if (mode == 1) {

// A villogó ikon megjelenítése

} else if (mode == 2) {

// A hang lejátszása

} else if (mode == 3) {

// Az ikon megjelenítése és a hang lejátszása

}

}

}

Most pedig térjünk át a statikus változók után a statikus tagfüggvények témakörére!

Statikus tagfüggvények

Az előzőekben megtanultuk, hogy a statikus változók olyan adatokat tárolnak, amelyek magához az osztályhoz kapcsolódnak. A statikus tagfüggvények szerepe hasonló: olyan műveleteteket valósítanak meg, amelyek a teljes osztályhoz, nem pedig annak példányaihoz köthetők. A Flash futásidejű API-jában találhatunk például egy Point nevű osztályt, amely egy Descartes-koordinátarendszerbeli pontot jelenít meg annak x és y koordinátájával. A Point osztály tartalmaz egy polar() nevű statikus tagfüggvényt, amely egy Point objektumot hoz létre megadott polárkoordináták (vagyis egy távolság és egy szög) alapján. A polárkoordináták átalakítása Descartes-koordinátákká olyan szolgáltatásnak tekinthető, ami általánosságban a Descartes-koordinátarendszerbeli pontokhoz tartozik, nem pedig az egyes Point objektumokhoz – következésképpen statikus tagfüggvényként határozzák meg.

A példánytagfüggvényekhez hasonlóan a statikus tagfüggvényeket is függvénymeghatározással hozhatjuk létre az osztály törzsében, itt azonban – ahogy az alábbi kódban is láthatjuk – fel kell tüntetnünk a static jellemzőt is:

class ValamilyenOsztály {

static function tagfüggvényNév (azonosító1 = érték1,

azonosító2 = érték2,

...

azonosítón = értékn) {

}

}

A példánytagfüggvényekhez hasonlóan a statikus tagfüggvények hozzáférhetőségét is módosíthatjuk elérésmódosítókkal. Itt is ugyanaz a négy típus – public, internal, protected és private – áll a rendelkezésünkre, és az alapértelmezés az internal (csomagszintű) elérés. Ha megadunk valamilyen elérésszabályozót, azt általában a static kulcsszó előtt helyezzük el:

class ValamilyenOsztály {

public static function tagfüggvényNév (azonosító1 = érték1,

azonosító2 = érték2,

...

azonosítón = értékn) {

}

}

A statikus tagfüggvények meghívásához az alábbi alakot használhatjuk:

ValamilyenOsztály.tagfüggvényNév(érték1, érték2,...értékn)

A fenti kódban a ValamilyenOsztály jelöli azt az osztályt, amelyikben a statikus tagfüggvényünket meghatároztuk, a tagfüggvényNév adja a tagfüggvény nevét, az érték1, érték2, ... értékn pedig az átadott paraméterek listáját. A tagfüggvényt meghatározó osztályon belül a tagfüggvényNév önmagában használható (az osztálynév és a pont feltüntetése nélkül). Vegyünk például egy A nevű osztályt, amelyben egy m statikus tagfüggvényt határozunk meg. Itt az A.m() kifejezés megegyezik az m()-mel. Mindazonáltal számos fejlesztő (és könyvünk is) használja az osztálynevet, hogy megkülönböztessék a statikus tagfüggvényeket a példánytagfüggvényektől.

Egyes osztályok kizárólag statikus tagfüggvényeik okán léteznek. Szerepük, hogy összefogják az összetartozó műveleteket, de példányok sosem jönnek létre belőlük. Ilyen például a Mouse osztály, amelynek a feladata kizárólag a show() és a hide() statikus tagfüggvények meghatározása, amelyek megjelenítik, illetve eltüntetik az egérmutatót. Elérésük közvetlenül a Mouse osztályon keresztül történik (például Mouse.hide()), nem pedig annak egy példányával. Ez olyannyira igaz, hogy a Mouse osztályból sosem jön létre objektum.

A statikus tagfüggvények rendelkeznek két korlátozással, amelyek a példánytagfüggvényekre nem vonatkoznak. Először is, a statikus tagfüggvények nem alkalmazhatják a this kulcsszót. Másodszor, a statikus tagfüggvények nem érhetik el saját osztályuk példányváltozóit és példánytagfüggvényeit (fordítva ez nem igaz, hiszen a példánytagfüggvények elérhetik a statikus változókat és tagfüggvényeket éppúgy, mint a példányváltozókat és egyéb példánytagfüggvényeket).

Általánosságban elmondható, hogy a statikus tagfüggvényeket ritkábban használják a statikus változóknál. Virtuális állatkertünkben például egyáltalán nem találhatunk statikus tagfüggvényt. Használatuk bemutatására ezért visszatérünk a 2. fejezetben bemutatott programocskánkhoz, amellyel az elektronikus levélcímeket ellenőriztük. Tételezzük fel, hogy alkalmazásunk olyan nagyra nőtt, hogy érdemes egy saját karakterlánckezelő osztályt létrehoznunk – legyen ez a StringUtils. Ebből az osztályból nem akarunk példányokat létrehozni, mindössze statikus tagfüggvényeket szeretnénk benne összegyűjteni. Példaképpen bemutatjuk egyikük, a contains() meghatározását, amely egy Boolean visszatérési értékkel jelzi, hogy a megadott karakterlánc tartalmaz-e egy bizonyos karaktert:

public class StringUtils {

public function contains (string, character) {

for (var i:int = 0; i <= string.length; i++) {

if (string.charAt(i) == character) {

return true;

}

}

return false;

}

}

A következő kódsorban bemutatjuk, miként használhatjuk ezt a tagfüggvényt annak vizsgálatára, hogy egy elektronikus levélcím tartalmazza-e a @ karaktert:

StringUtils.contains("me@moock.org", "@");

Természetesen egy valódi alkalmazásban a címet a felhasználó adja meg, és a contains() segítségével dönthetünk arról, hogy elküldjük-e az űrlap tartalmát a kiszolgálónak. Lássunk most egy életszerűbb helyzetet:

if (StringUtils.contains(userEmail, "@")) {

// Kód az űrlap adatainak elküldésére

} else {

// "Érvénytelen adat" üzenet megjelenítése

}

Saját statikus tagfüggvényeink mellett az ActionScript is létrehoz egyet. Ez a tagfüggvény, amely minden osztályban megjelenik, az osztálybeállító tagfüggvény névre hallgat.

Az osztálybeállító tagfüggvény

Amikor az ActionScript futásidőben meghatároz egy osztályt, automatikusan létrehoz és végre is hajt egy különleges tagfüggvényt – ez az osztálybeállító tagfüggvény. Itt kapnak helyet el az osztály statikus változóinak kezdeti értékadásai, és minden olyan osztályszintű kód, amely nem tagfüggvényt vagy változót határoz meg.

Az osztálybeállító tagfüggvény lehetőséget ad arra, hogy az osztály létrehozásakor egyszeri műveleteket hajtsunk végre, esetleg az osztályon kívüli változók vagy tagfüggvények használatával. Tegyük fel, hogy egy olyan alkalmazást szeretnénk készíteni, amellyel a felhasználóink elolvashatják az elektronikus leveleiket – méghozzá olyan környezetben, amely megfelel az operációs rendszer felületi megjelenésének. Központi osztályunk, a MailReader ezért az osztálybeállító tagfüggvényében megvizsgálja, hogy milyen grafikus témát alkalmaz az operációs rendszer, és az eredményt a theme nevű statikus változóban tárolja – ez határozza majd meg a teljes alkalmazás grafikai megjelenését. Az alábbi kódban bemutatjuk a MailReader osztálybeállító tagfüggvényét. Az operációs rendszer vizsgálatára a flash.system.Capabilities beépített osztály os statikus változóját használjuk.

package {

import flash.system.*;

 

public class MailReader {

static var theme;

if (Capabilities.os == "MacOS") {

theme = "MAC";

} else if (Capabilities.os == "Linux") {

theme = "LINUX";

} else {

theme = "WINDOWS";

}

}

}

Az osztálybeállító tagfüggvény kódja értelmezett módban fut, tehát a JIT fordító nem dolgozza fel. Ezt azért érdemes tudnunk, mivel a JIT-fordítású kód jelentősen gyorsabban fut, mint az értelmezett kód, így a számításigényesebb kódrészleteket érdemes máshova helyeznünk, ha a sebesség lényeges szempont.

Class objektumok

A korábbiakban megtanultuk, hogy a statikus változókat és tagfüggvényeket az őket meghatározó osztályokon keresztül érhetjük el. Így ha például a maxCalories nevű statikus változót szeretnénk elérni, amelyet a VirtualPet osztályban határoztunk meg, a következőt írhatjuk:

VirtualPet.maxCalories

A fenti kódban a VirtualPet név használata nem egyszerű programnyelvi fordulat, mint ahogy az előzőek alapján hihetnénk – itt valóban egy objektumra hivatkozunk, méghozzá a beépített Class osztály egy példányára.

Futásidőben ugyanis az ActionScript minden osztályát a Class osztály egy objektuma jeleníti meg. A programozó szempontjából a Class objektumok elsősorban a statikus változók és tagfüggvények elérésére használatosak. Mindazonáltal, más objektumokhoz hasonlóan a Class osztály példányai is értékek, amelyeket változókhoz rendelhetünk, tagfüggvényeknek és függvényeknek adhatunk át, vagy éppen kaphatunk vissza tőlük. Ezt használjuk ki VirtualZoo osztályunk alábbi, módosított változatában, ahol a VirtualPet osztályt megadó Class objektumot egy vp nevű változóhoz rendeljük, majd ennek a változónak a segítségével hozunk létre egy új VirtualPet objektumot.

package zoo {

public class VirtualZoo {

private var pet;

 

public function VirtualZoo ( ) {

var vp = VirtualPet;

pet = new vp("Stan");

}

}

}

Ezt a módszert használhatjuk akkor is, ha egy .swf fájlból egy másik .swf fájl osztályait szeretnénk elérni, vagy amikor külső erőforrásokat (például képeket vagy betűtípusokat) szeretnénk beágyazni egy .swf fájlból. Mindezeket részletesebben könyvünk II. részében tárgyaljuk.

Ezzel a statikus változók és tagfüggvények témakörének végére értünk. Mielőtt azonban továbblépnénk a következő fejezetre, érdemes megállnunk egy percre, és összevetnünk néhány, az eddigiekben megismert kifejezést a Java, valamint a C++ nyelvekben használt megfelelőikkel.

A C++ és a Java szóhasználata

A példányváltozók és -tagfüggvények, valamint a statikus változók és tagfüggvények fogalma jelen van a legtöbb objektumközpontú programozási nyelvben. Nem kivétel ez alól a C++ és a Java sem, amelyeknek a megfelelő kifejezéseit a 4.1. táblázatban vethetjük össze az ActionScriptben megismertekkel.

4.1. táblázat Elnevezések a különböző programozási nyelvekben

ActionScript Java C++

példányváltozó mező vagy példányváltozó adattag

példánytagfüggvény tagfüggvény tagfüggvény

statikus változó osztályváltozó statikus adattag

statikus tagfüggvény osztálytagfüggvény statikus tagfüggvény

Irány a függvények világa!

Megtanultuk, hogy a példánytagfüggvények az objektumok, a statikus tagfüggvények pedig az osztályok viselkedését határozzák meg. A következőkben megismerkedünk a függvényekkel, amelyek önmagukban álló – nem osztályokhoz vagy objektumokhoz rendelt – műveleteket írnak le.

Vissza a könyv részletes adataihoz

Legutóbb látogatott oldalaim

Keresések Könyvek, termékek Kategóriák