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