Klassen Array är en av de få huvudklasserna som inte är slutgiltig, vilket innebär att du kan skapa egna underklasser till klassen Array. I detta avsnitt finns exempel på hur du skapar en underklass till klassen Array. Dessutom beskrivs något av vad som kan hända under processens gång.
Som nämnts tidigare är arrayer i ActionScript inte ”typade”, men du kan skapa en underklass av klassen Array som endast accepterar element med en viss datatyp. I exemplen i följande avsnitt definieras en Array-underklass med namnet TypedArray, där endast värden som innehåller datatypen som angavs i den första parametern tas emot. Klassen TypedArray visas bara som ett exempel på hur du kan utöka klassen Array och är av många anledningar inte lämplig i verkliga tillämpningar. För det första görs typkontrollen under körningen och inte vid kompileringen. För det andra ignoreras felmatchningen om den upptäcks i en TypedArray-metod och inget undantag genereras trots att metoden enkelt kan förändras så att detta görs. För det tredje kan klassen inte förhindra att arrayåtkomstoperatorn infogar värden med någon annan datatyp i arrayen. Slutligen favoriserar kodstilen enkelhet framför optimal prestanda.
Obs!
Du kan använda den teknik som beskrivs här när du skapar en typbestämd array. Det är emellertid bättre att använda ett Vector-objekt. En Vector-instans är en äkta typbestämd array, som har bättre prestanda och fler fördelar än klassen Array och underklasser. Syftet här är att visa hur du skapar en Array-underklass.
Deklarera underklasser
Använd nyckelordet
extends
för att visa att en klass är en underklass till Array. I en Array-underklass ska attributet
dynamic
användas precis som i klassen Array. Annars fungerar underklassen inte som den ska.
I följande exempel visas definitionen av klassen TypedArray, som innehåller en konstant för datatypen, en konstruktormetod och fyra metoder för att lägga till element till arrayen. Koden för varje metod har utelämnats i detta exempel, men den beskrivs och förklaras i sin helhet i avsnitten som följer:
public dynamic class TypedArray extends Array
{
private const dataType:Class;
public function TypedArray(...args) {}
AS3 override function concat(...args):Array {}
AS3 override function push(...args):uint {}
AS3 override function splice(...args) {}
AS3 override function unshift(...args):uint {}
}
I alla de fyra åsidosatta metoderna används AS3-namnutrymmet i stället för attributet
public
, eftersom vi i detta exempel antar att kompilatoralternativet
-as3
är
true
och att kompilatoralternativet
-es
är
false
. Detta är standardinställningarna för Adobe Flash Builder och AdobeFlashProfessional.
Om du är en van utvecklare, som föredrar att använda prototyparv, kan du göra två smärre förändringar i klassen TypedArray så att den kan kompileras med kompilatoralternativet
-es
som
true
. Börja med att ta bort alla förekomster av attributet
override
och ersätt AS3-namnutrymmet med attributet
public
. Därefter ersätter du alla fyra förekomster av
super
med
Array.prototype
.
Konstruktorn TypedArray
Underklasskonstruktorn utgör en intressant utmaning eftersom den måste kunna godta en argumentlista med godtycklig längd. Utmaningen är hur argumenten ska överföras till superkonstruktorn så att arrayen skapas. När du skickar en argumentlista som en array, betraktas den av superkonstruktorn som ett enskilt argument av typen Array och den resulterande arrayen kommer då alltid att vara ett element långt. Det traditionella sättet att hantera överförda argumentlistor är att använda metoden
Function.apply()
, som tar en array med argument som dess andra parameter men konverterar den till en argumentlista när funktionen körs. Tyvärr går det inte att använda metoden
Function.apply()
tillsammans med en konstruktor.
Det enda som återstår att göra är att återskapa logiken i Array-konstruktorn i konstruktorn TypedArray. I nästa exempel visas algoritmen som används i klassen Arrays konstruktor, som sedan återanvänds i din Array-underklasskonstruktor:
public dynamic class Array
{
public function Array(...args)
{
var n:uint = args.length
if (n == 1 && (args[0] is Number))
{
var dlen:Number = args[0];
var ulen:uint = dlen;
if (ulen != dlen)
{
throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")");
}
length = ulen;
}
else
{
length = n;
for (var i:int=0; i < n; i++)
{
this[i] = args[i]
}
}
}
}
Koden i konstruktorn TypedArray har mycket gemensamt med koden för Array-konstruktorn, men fyra ändringar måste göras. Till att börja med innehåller parameterlistan en ny obligatorisk parameter med typen Class, som gör att det går att ange arrayens datatyp. För det andra är datatypen som skickas till konstruktorn tilldelad till variabeln
dataType
. För det tredje är värdet för egenskapen
length
i
else
-satsen tilldelat efter
for
-slingan så att
length
endast kommer att innehålla argument med rätt typ. Slutligen används i texten i
for
-slingan den version av metoden
push()
som åsidosätter argument så att endast de med rätt datatyp läggs till i arrayen. I följande exempel visas konstruktorfunktionen TypedArray:
public dynamic class TypedArray extends Array
{
private var dataType:Class;
public function TypedArray(typeParam:Class, ...args)
{
dataType = typeParam;
var n:uint = args.length
if (n == 1 && (args[0] is Number))
{
var dlen:Number = args[0];
var ulen:uint = dlen
if (ulen != dlen)
{
throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")")
}
length = ulen;
}
else
{
for (var i:int=0; i < n; i++)
{
// type check done in push()
this.push(args[i])
}
length = this.length;
}
}
}
TypedArray – metoder för åsidosättande
Klassen TypedArray åsidosätter fyra av klassen Arrays metoder för att lägga till element till en array. I vart och ett av fallen används i den åsidosättande metoden en typkontroll som förhindrar att element med fel datatyp läggs till. Detta innebär att varje metod anropar den egna överordnade klassens version.
Metoden
push()
itererar igenom argumentlistan med en
for..in
-slinga och en typkontroll görs av varje argument. De argument som inte har rätt typ tas bort från arrayen
args
med metoden
splice()
. När
for..in
-slingan är slut innehåller arrayen
args
endast värden med typen
dataType
. Den överordnade klassens version av
push()
anropas sedan med den uppdaterade versionen av arrayen
args
, vilket visas i följande exempel:
AS3 override function push(...args):uint
{
for (var i:* in args)
{
if (!(args[i] is dataType))
{
args.splice(i,1);
}
}
return (super.push.apply(this, args));
}
Med metoden
concat()
skapas en temporär TypedArray med namnet
passArgs
. Den används för att lagra argument som har accepterats i typkontrollen. Detta medger att det går att återanvända koden för typkontrollen i metoden
push()
. Med en
for..in
-slinga itererar du genom
args
-arrayen och anropar
push()
vid varje argument. Eftersom
passArgs
har typen TypedArray körs TypedArray-versionen av
push()
. Metoden
concat()
anropar sedan den egna överordnade klassens version, vilket framgår i följande exempel:
AS3 override function concat(...args):Array
{
var passArgs:TypedArray = new TypedArray(dataType);
for (var i:* in args)
{
// type check done in push()
passArgs.push(args[i]);
}
return (super.concat.apply(this, passArgs));
}
Metoden
splice()
tar en godtycklig argumentlista, men de två första argumenten refererar alltid till ett indexvärde och antalet element som ska tas bort. Detta är orsaken till att det görs en typkontroll av elementen i arraken
args
endast vid indexposition 2 och högre i den åsidosatta metoden
splice()
. En intressant aspekt av koden är att det verkar finnas ett rekursivt anrop till
splice()
i
for
-slingan, men detta är trots allt inget rekursivt anrop eftersom
args
har type Array och inte TypedArray. Detta innebär att anropet
args.splice()
är ett anrop till den överordnade klassens version av metoden. När
for..in
-slingan är klar innehåller arrayen
args
endast värden med rätt typ i indexpositionerna 2 eller högre och
splice()
-anropet är till den egna överordnade klassens version, vilket visas i följande exempel:
AS3 override function splice(...args):*
{
if (args.length > 2)
{
for (var i:int=2; i< args.length; i++)
{
if (!(args[i] is dataType))
{
args.splice(i,1);
}
}
}
return (super.splice.apply(this, args));
}
För metoden
unshift()
, där element läggs till i början av en array, accepteras också en godtycklig argumentlista. I den åsidosatta metoden
unshift()
används en algoritm som mycket påminner om den som används i metoden
push()
, vilket visas i följande exempel:
AS3 override function unshift(...args):uint
{
for (var i:* in args)
{
if (!(args[i] is dataType))
{
args.splice(i,1);
}
}
return (super.unshift.apply(this, args));
}
}