Extension de la classe Array

Flash Player 9 et les versions ultérieures, Adobe AIR 1.0 et les versions ultérieures

La classe Array est l’une des classes de base non finale, c’est-à-dire que vous pouvez créer votre sous-classe d’Array. Cette section décrit comment créer une sous-classe d’Array et décrit les problèmes pouvant se poser pendant le processus.

Comme mentionné précédemment, les tableaux dans ActionScript ne sont pas typés, mais vous pouvez créer une sous-classe d’Array qui accepte des éléments d’un seul type de données. L’exemple fourni dans les sections suivantes définit une sous-classe Array appelée TypedArray qui limite ses éléments à des valeurs du type de données indiqué dans le premier paramètre. La classe TypedArray est présentée comme un exemple de la façon dont la classe Array est étendue et risque de ne pas être adaptée à des fins de production pour différentes raisons. Premièrement, la vérification du type a lieu lors de l’exécution plutôt que de la compilation. Deuxièmement, lorsqu’une méthode TypedArray rencontre une incompatibilité, elle est ignorée et aucune exception n’est renvoyée, même si vous pouvez facilement modifier les méthodes pour renvoyer des exceptions. Troisièmement, la classe ne peut pas empêcher l’utilisation de l’opérateur d’accès au tableau pour insérer des valeurs de n’importe quel type dans le tableau. Quatrièmement, le style de codage privilégie la simplicité par rapport à l’optimisation des performances.

Remarque : vous pouvez utiliser la technique décrite ici pour créer un tableau typé. Cependant, utiliser un objet Vector constitue une meilleure démarche. Une occurrence de Vector est un véritable tableau typé. Elle dépasse la classe Array ou toute sous-classe par ses performances et ses améliorations. Une illustration de la création d’une sous-classe Array constitue l’objet de cette description.

Déclaration de la sous-classe

Utilisez le mot-clé extends pour indiquer qu’une classe est une sous-classe d’Array. Une sous-classe d’Array doit utiliser l’attribut dynamic , comme la classe Array. Autrement, votre sous-classe ne fonctionne pas correctement.

Le code suivant représente la définition de la classe TypedArray, qui comporte une constante contenant le type de données, une méthode de constructeur et les quatre méthodes permettant d’ajouter des éléments au tableau. Le code pour chaque méthode est omis dans cet exemple, mais il est décrit de façon détaillée dans les sections qui suivent :

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

Les quatre méthodes de remplacement utilisent l’espace de nom AS3 au lieu de l’attribut public car cet exemple suppose que l’option -as3 du compilateur est définie sur true et l’option -es du compilateur sur false . Il s’agit des paramètres par défaut pour Adobe Flash Builder et AdobeFlashProfessional.

Si vous êtes un développeur expérimenté et que vous préférez utiliser l’héritage de prototype, vous pouvez apporter deux changements mineurs à la classe TypedArray afin qu’elle compile avec l’option -es du compilateur définie sur true . Commencez par supprimer toutes les occurrences de l’attribut override et remplacez l’espace de nom AS3 par l’attribut public . Remplacez ensuite Array.prototype pour les quatre occurrences de super .

Constructeur TypedArray

Le constructeur de sous-classe pose un défi intéressant car il doit accepter une liste d’arguments de longueur arbitraire. Il s’agit de savoir comment transférer les arguments au superconstructeur pour créer le tableau. Si vous transmettez la liste des arguments sous forme d’un tableau, le superconstructeur le considère comme un seul argument de type Array et le tableau résultant a toujours une longueur d’1 élément. Le transfert de listes d’arguments se fait généralement au moyen de la méthode Function.apply() , qui prend un tableau d’arguments comme second paramètre mais le convertit en une liste d’arguments lors de l’exécution de la fonction. Malheureusement, vous ne pouvez pas utiliser la méthode Function.apply() avec des constructeurs.

La seule solution est de recréer la logique du constructeur Array dans le constructeur TypedArray. Le code suivant indique l’algorithme utilisé dans le constructeur de classe Array, que vous pouvez réutiliser dans votre constructeur de sous-classe 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]  
            } 
        } 
    } 
}

Le constructeur TypedArray partage une grande partie du code du constructeur Array, avec seulement quatre changements apportés au code. Premièrement, la liste des paramètres comprend un nouveau paramètre obligatoire de type Class qui permet d’indiquer le type de données du tableau. Deuxièmement, le type de données transmis au constructeur est affecté à la variable dataType . Troisièmement, dans l’instruction else , la valeur de la propriété length est affectée après la boucle for de façon à ce que length comprenne uniquement des arguments du type correct. Quatrièmement, le corps de la boucle for utilise la version de remplacement de la méthode push() de façon à ce que seuls des arguments du type de données correct soient ajoutés au tableau. L’exemple suivant présente la fonction constructeur 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; 
        } 
    } 
}

Méthodes de remplacement TypedArray

La classe TypedArray remplace les quatre méthodes de la classe Array qui permettent d’ajouter des éléments à un tableau. Dans chaque cas, la méthode de remplacement ajoute une vérification du type qui empêche d’ajouter des éléments qui ne sont pas du type de données correct. Chaque méthode appelle ensuite sa version de superclasse.

La méthode push() parcourt en boucle la liste des arguments avec une boucle for..in et effectue une vérification du type sur chaque argument. Les arguments qui ne sont pas de type correct sont supprimés du tableau args avec la méthode splice() . Une fois que la boucle for..in se termine, le tableau args contient des valeurs de type dataType uniquement. La version de superclasse de push() est ensuite appelée avec le tableau args mis à jour, comme indiqué dans le code suivant :

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

La méthode concat() crée un TypedArray temporaire appelé passArgs pour stocker les arguments soumis à la vérification de type. Ceci permet de réutiliser le code de vérification de type qui existe dans la méthode push() . Une boucle for..in effectue une itération sur le tableau args et appelle push() sur chaque argument. Etant donné que passArgs est typé sous la forme TypedArray, la version TypedArray de push() est exécutée. La méthode concat() appelle ensuite sa version de superclasse, comme indiqué dans le code suivant :

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

La méthode splice() prend une liste d’arguments arbitraire, mais les deux premiers arguments se réfèrent toujours à un numéro d’index et au nombre d’éléments à supprimer. C’est pourquoi la méthode de remplacement splice() effectue la vérification de type uniquement pour les éléments du tableau args dans les positions d’index 2 ou supérieures. Il est intéressant de noter que dans le code, il semble y avoir un appel récursif à splice() à l’intérieur de la boucle for , mais en réalité, ce n’est pas le cas car args est de type Array et non de type TypedArray, ce qui signifie que l’appel à args.splice() est un appel à la version de superclasse de la méthode. Une fois que la boucle for..in se termine, le tableau args contient des valeurs du type correct uniquement dans les positions d’index 2 ou supérieures, et splice() appelle sa version de superclasse, comme indiqué dans le code suivant :

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

La méthode unshift() , qui ajoute des éléments au début d’un tableau, accepte une liste d’arguments arbitraire également. La méthode de remplacement unshift() utilise un algorithme très semblable à celui utilisé par la méthode push() , comme indiqué dans le code suivant :

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