A classe Array é uma das poucas classes principais que não é final, ou seja, você pode criar sua própria subclasse de Array. Esta seção mostra um exemplo de como criar uma subclasse de Array e discute alguns problemas que podem ocorrer durante o processo.
Como mencionado anteriormente, as matrizes no ActionScript não são tipificadas, mas é possível criar uma subclasse de Array que aceita elementos somente de um tipo de dados específico. O exemplo das seções a seguir define uma subclasse de Array chamada TypedArray que limita seus elementos aos valores do tipo de dados especificado no primeiro parâmetro. A classe TypedArray é apresentada simplesmente como um exemplo que mostra como estender a classe Array e talvez não seja adequada para fins de produção por diversos motivos. Primeiro, a verificação de tipo ocorre durante o tempo de execução, não no tempo de compilação. Segundo, quando um método TypedArray encontra uma discordância, esta é ignorada e nenhuma exceção é lançada, embora os métodos possam ser facilmente modificados para lançar exceções. Terceiro, a classe não pode impedir o uso do operador de acesso à matriz para inserir valores de qualquer tipo na matriz. Quarto, o estilo de codificação favorece a simplicidade por meio da otimização do desempenho.
Nota:
Você pode usar a técnica descrita aqui para criar uma matriz tipificada. No entanto, recomenda-se o uso de um objeto Vector. Uma ocorrência de Vector é uma matriz true type e fornece desempenho e outros aprimoramentos em relação à classe Array ou qualquer subclasse. O objetivo desta discussão é demonstrar como criar uma subclasse Array.
Declaração da subclasse
Use a palavra-chave
extends
para indicar que uma classe é subclasse de Array. Uma subclasse de Array deve usar o atributo
dynamic
, assim como a classe Array. Caso contrário, a subclasse não funcionará adequadamente.
O código a seguir mostra a definição da classe TypedArray, que contém uma constante para armazenar o tipo de dados, um método de construtor e os quatro métodos que permitem adicionar elementos à matriz. O código de cada método é omitido neste exemplo, mas é descrito e explicado em detalhes nas seções a seguir:
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 {}
}
Os quatro métodos de substituição usam o espaço para nomes AS3 em vez do atributo
public
porque este exemplo supõe que a opção
-as3
do compilador está definida como
true
e a opção
-es
está definida como
false
. Essas são as configurações padrão do Adobe Flash Builder e do Adobe® Flash® Professional.
Se você for um desenvolvedor experiente que prefere usar protótipos herdados, faça duas pequenas alterações na classe TypedArray para compilá-la com a opção
-es
do compilador definida como
true
. Primeiro, remova todas as ocorrências do atributo
override
e substitua o espaço para nomes AS3 pelo atributo
public
. Segundo, substitua
Array.prototype
nas quatro ocorrências de
super
.
construtor TypedArray
O construtor de subclasse cria um desafio interessante porque aceita uma lista de argumentos de comprimento arbitrário. O desafio agora é transmitir os argumentos para o superconstrutor a fim de criar a matriz. Se você transmitir a lista de argumentos como uma matriz, o superconstrutor irá considerá-la como um único argumento do tipo Array e a matriz resultante terá sempre um elemento. O modo tradicional de manipular a transmissão de listas de argumentos é usar o método
Function.apply()
, que trata uma matriz de argumentos como seu segundo parâmetro, mas a converte em uma lista de argumentos ao executar a função. Infelizmente, o método
Function.apply()
não pode ser usado com construtores.
A única opção restante é recriar a lógica do construtor Array no construtor TypedArray. O código a seguir mostra o algoritmo usado no construtor da classe Array, que pode ser reutilizado no construtor da subclasse de 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]
}
}
}
}
O construtor TypedArray compartilha a maior parte do código do construtor Array, com apenas quatro alterações no código. Primeiro, a lista de parâmetros inclui um novo parâmetro obrigatório de classe de tipo que permite especificar o tipo de dados da matriz. Segundo, a variável
dataType
é atribuída ao tipo de dados transmitido ao construtor. Terceiro, na instrução
else
, o valor da propriedade
length
é atribuído depois do loop
for
para que
length
inclua somente os argumentos do tipo correto. Quarto, o corpo do loop
for
usa a versão de substituição do método
push()
para que apenas os argumentos do tipo de dados correto sejam adicionados à matriz. O exemplo a seguir mostra a função do construtor 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étodos de substituição de TypedArray
A classe TypedArray substitui os quatro métodos da classe Array que permitem adicionar elementos a uma matriz. Em cada caso, o método de substituição adiciona uma verificação de tipo que impede a adição de elementos que não são do tipo de dados correto. Posteriormente, cada método chama sua própria versão de superclasse.
O método
push()
percorre a lista de argumentos com um loop
for..in
e faz uma verificação de tipo em cada argumento. Todos os argumentos que não são do tipo correto são removidos da matriz
args
com o método
splice()
. Quando o loop
for..in
termina, a matriz
args
contém valores somente do tipo
dataType
. A versão de superclasse de
push()
é chamada com a matriz
args
atualizada, como mostra o código a seguir:
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));
}
O método
concat()
cria uma matriz TypedArray temporária chamada
passArgs
para armazenar os argumentos aprovados na verificação de tipo. Isso permite a reutilização do código de verificação de tipo existente no método
push()
. O loop
for..in
percorre a matriz
args
e chama
push()
em cada argumento. Como
passArgs
é tipificada como TypedArray, a versão TypedArray de
push()
é executada. Em seguida, o método
concat()
chama sua própria versão de superclasse, como mostra o código a seguir:
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));
}
O método
splice()
usa uma lista arbitrária de argumentos, mas os dois primeiros argumentos sempre fazem referência a um número de índice e ao número de elementos a serem excluídos. É por esse motivo que o método de substituição
splice()
executa a verificação de tipo somente para os elementos da matriz
args
nas posições de índice 2 ou superior. É interessante observar que o código parece ser uma chamada recursiva para
splice()
no loop
for
, mas não é porque
args
é do tipo Array e não TypedArray, o que significa que a chamada para
args.splice()
é uma chamada para a versão de superclasse do método. Quando o loop
for..in
termina, a matriz
args
contém apenas os valores do tipo correto nas posições de índice 2 ou superior e
splice()
chama sua própria versão de superclasse, como mostra o código a seguir:
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));
}
O método
unshift()
, que adiciona elementos ao início de uma matriz, também aceita uma lista arbitrária de argumentos. O método de substituição
unshift()
usa um algoritmo muito parecido com o usado pelo método
push()
, como mostra o exemplo a seguir:
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));
}
}