De klasse Array uitbreiden

Flash Player 9 of hoger, Adobe AIR 1.0 of hoger

De klasse Array is een van de weinige kernklassen die niet definitief is. Dit betekent dat u voor Array uw eigen subklassen kunt maken. In deze sectie wordt een voorbeeld gegeven van de manier waarop u een subklasse voor Array kunt maken. Ook komen enkele problemen aan bod die hierbij kunnen optreden.

Zoals eerder is vermeld, worden arrays in ActionScript niet getypeerd. Het is echter wel mogelijk om een subklasse voor Array te maken die alleen elementen van een specifiek gegevenstype accepteert. In het voorbeeld in de volgende secties wordt voor Array een subklasse met de naam TypedArray gedefinieerd die de elementen beperkt tot waarden van het gegevenstype dat in de eerste parameter is opgegeven. De klasse TypedArray wordt hier slechts gegeven als voorbeeld van hoe de klasse Array kan worden uitgebreid. Deze manier van werken is om diverse redenen mogelijk niet geschikt voor productiewerk. Ten eerste vindt controle van het type plaats tijdens het uitvoeren en niet tijdens het compileren. Ten tweede: wanneer een methode TypedArray gegevens aantreft die niet overeenkomen, wordt dit genegeerd en wordt geen uitzondering gegenereerd, hoewel de methoden gemakkelijk kunnen worden aangepast om wel uitzonderingen te genereren. Ten derde kan de klasse niet voorkomen dat de operator voor arraytoegang wordt gebruikt om waarden van een willekeurig type in te voegen in de array. Ten vierde is deze manier van coderen meer gericht op eenvoud dan optimale prestaties.

Opmerking: Met de hier beschreven technieken kunt u een array met type maken. Het is echter beter om daarvoor een Vector-object te gebruiken. Een Vector-instantie is een echte array met type, en biedt betere prestaties en andere voordelen ten opzichte van de klasse Array of een subklasse. Het doel van deze discussie is aan te tonen hoe een subklasse van de klasse Array wordt gemaakt.

De subklasse declareren

Gebruik het trefwoord extends om aan te geven dat een klasse een subklasse van Array is. Een subklasse van Array moet het kenmerk dynamic gebruiken (net als de klasse Array). Anders werkt de subklasse niet goed.

De volgende code toont hoe de klasse TypedArray wordt gedefinieerd, met daarin een constante voor het gegevenstype, een constructormethode en de vier methoden waarmee elementen aan de array kunnen worden toegevoegd. In dit voorbeeld wordt de code voor de verschillende methoden weggelaten. In latere secties wordt een en ander wel volledig uitgelegd:

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

De vier overschreven methoden gebruiken allemaal de naamruimte AS3 in plaats van het kenmerk public , omdat er in dit voorbeeld van wordt uitgegaan dat de compileroptie -as3 op true en -es op false staat. Dit zijn standaardinstellingen voor Adobe Flash Builder en voor AdobeFlashProfessional.

Als u een gevorderd ontwikkelaar bent die liever werkt met prototypeovererving, kunt u de klasse TypedArray op twee kleine punten aanpassen, zodat wordt gecompileerd met de compileroptie -es op true . Eerst verwijdert u het kenmerk override overal waar het voorkomt en vervangt u de naamruimte AS3 door het kenmerk public . Vervolgens gebruikt u Array.prototype voor alle vier de keren dat super voorkomt.

De constructor TypedArray

De subklassenconstructor vormt een interessante uitdaging, omdat de constructor een lijst met argumenten van willekeurige lengte moet accepteren. De uitdaging zit hem in de manier waarop de argumenten worden doorgegeven aan de superconstructor om de array te maken. Als u de lijst met argumenten als een array doorgeeft, wordt deze gezien als één argument van het type Array en is de resulterende array altijd 1 element lang. De traditionele manier om argumentenlijsten door te geven is met de methode Function.apply() . In deze methode vormt een array met argumenten een tweede parameter die wordt geconverteerd naar een lijst met argumenten wanneer de functie wordt uitgevoerd. Helaas kan de methode Function.apply() niet worden gebruikt bij constructors.

De enige resterende mogelijkheid is dat de logica van de constructor Array wordt nagemaakt in de constructor TypedArray. De volgende code laat de algoritme zien die in de klassenconstructor Array wordt gebruikt en die u ook kunt gebruiken in uw constructor voor een subklasse voor Array:

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

De constructor TypedArray heeft de meeste code gemeenschappelijk met de constructor Array. Er zijn slechts op vier punten verschillen. Ten eerste bevat de parameterlijst een nieuwe verplichte parameter van het type Class waarmee het gegevenstype van de arrays kan worden opgegeven. Vervolgens wordt het gegevenstype dat aan de constructor wordt doorgegeven toegewezen aan de variabele dataType . Daarna wordt in de instructie else de waarde van de eigenschap length toegewezen na de lus for , zodat length alleen argumenten van het juiste type bevat. Ten slotte wordt in de body van de lus for de overschreven versie van de methode push() gebruikt, zodat alleen argumenten van het juiste gegevenstype aan de array worden toegevoegd. Het volgende voorbeeld laat de constructorfunctie TypedArray zien:

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

Door TypedArray overschreven methoden

De klasse TypedArray overschrijft de vier methoden van de klasse Array die in staat zijn om elementen toe te voegen aan een array. In elk geval voegt de overschreven methode een typecontrole toe waarmee het toevoegen van elementen van het verkeerde gegevenstype wordt voorkomen. Vervolgens roept elke methode de superklassenversie van zichzelf aan.

De methode push() doorloopt de lijst met argumenten met een lus for..in , waarbij voor elk argument een typecontrole wordt uitgevoerd. Alle argumenten die niet van het juiste type zijn, worden uit de array args verwijderd met de methode splice() . Wanneer de lus for..in is voltooid, bevat de array args alleen waarden van het type dataType . Vervolgens wordt de superklassenversie van push() aangeroepen met de bijgewerkte array args , zoals in de volgende code:

    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)); 
    }

De methode concat() maakt een tijdelijke TypedArray met de naam passArgs om de argumenten op te slaan die de typecontrole doorstaan. Dit maakt het mogelijk om de typecontrolecode die in de methode push() aanwezig is opnieuw te gebruiken. Met een lus for..in wordt de array args doorlopen en wordt push() voor elk argument aangeroepen. Omdat passArgs wordt getypeerd als TypedArray, wordt de TypedArray-versie van push() uitgevoerd. De methode concat() roept vervolgens zijn eigen superklassenversie aan, zoals in de volgende code:

    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)); 
    }

De methode splice() neemt een willekeurige lijst met argumenten, maar de eerste twee argumenten verwijzen altijd naar een indexnummer en het aantal elementen dat moet worden verwijderd. Daarom voert de overschreven methode splice() alleen een controle van het type uit voor elementen in de array args op indexpositie 2 of hoger. Interessant aan de code is dat er een recursieve aanroep van splice() lijkt te zijn in de lus for . Dit is echter geen recursieve aanroep, omdat args van het type Array is in plaats van TypedArray. Dit betekent dat het aanroepen van args.splice() een aanroep is naar de superklassenversie van de methode. Wanneer de lus for..in is voltooid, bevat de array args alleen waarden van het juiste type op indexpositie 2 of hoger en roept splice() zijn eigen superklassenversie aan, zoals in de volgende code wordt getoond:

    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)); 
    }

De methode unshift() , waarmee elementen worden toegevoegd aan het begin van een array, accepteert ook een willekeurige lijst met argumenten. De overschreven methode unshift() gebruikt een algoritme die veel lijkt op het algoritme dat door de methode push() wordt gebruikt, zoals in het volgende codevoorbeeld wordt getoond:

    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)); 
    } 
}