Extensão da classe Array

Flash Player 9 e posterior, Adobe AIR 1.0 e posterior

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