Instanciando classe em tempo de execução
September 19th, 2009
Todo mundo já passou por uma situação em que a instanciação dos objetos deveria ser dinâmica. Uma das soluções, porém nada prática e profissional, é fazer um switch() ou if-elseif-else.
Isso é comum quando temos um menu que leva para algumas áreas. Poderíamos instanciar esse objeto de uma forma dinâmica. Um loop que instancia um objeto e armazena essas referências num Dictionary ou Array.
São inúmeras situações em que precisamos de instanciação em tempo de execução, e é isso que vamos ver agora.
Objetivo
Precisamos instanciar 5 classes, guardar sua referência num objeto e usá-lo depois.
Exemplo
- Crie um AS3 Project chamado GetDefinitionByName (FlashDevelop)
- Crie 5 classes: Bola1.as, Bola2.as, Bola3.as, etc
Para instanciar uma classe é muito simples, basta fazermos:
var bola:Bola1 = new Bola1();
Ao colocarmos a keyword new, estamos dizendo a AVM (ActionScript Virtual Machine) que queremos iniciar uma nova instância da classe Bola1. Como a classe Bola1 está no mesmo pacote que a Main, não precisamos especificar seu caminho. Caso estivesse em outro pacote, ou teríamos que importar, ou colocar o nome completo na hora de instanciar, por exemplo:
var bola:meu.pacote.Bola1 = new meu.pacote.Bola1();
Sabendo disso, vamos instanciar a Bola1 dinamicamente:
var bola:Bola1 = getDefinitionByName("Bola1");
Vai explodir um erro.
D:\pessoal\blog\GetDefinitionByName\src\Main.as(25): col: 21 Error: Implicit coercion of a value with static type Object to a possibly unrelated type Bola1.
O tipo de nossa variavel está errado. Isso por que a função getDefinitionByName retorna uma referência de classe. Ou seja, sua classe, ao ser compilada, está na memória da AVM para poder ser instanciada. Como getDefinitionByName retorna uma referência a sua classe, temos que tipá-la com o que ela realmente é: uma classe.
var referenciaBola:Class = getDefinitionByName("Bola1");
Irá explodir outro erro.
D:\pessoal\blog\GetDefinitionByName\src\Main.as(25): col: 31 Error: Implicit coercion of a value with static type Object to a possibly unrelated type Class.
Isso ocorre pois precisamos avisar ao compilador que ele estará nos retornando uma classe através do método getDefinitionByName();
var referenciaBola:Class = getDefinitionByName("Bola1") as Class;
Ok, compilado. Agora temos nossa referência de classe. Podemos instanciar nossa classe Bola1 através da nossa instância de referência de classe referenciaBola.
var referenciaBola:Class = getDefinitionByName("Bola1") as Class; var bola:Bola1 = new referenciaBola() as Bola1; addChild(bola);
Pronto, instanciamos a classe Bola1 dinamicamente. Podemos compilar.
Fazendo o loop
Vamos colocar nosso código dentro de um loop, guardar a referência das bolas dentro de um array e fazer qualquer coisa com elas.
for (var i:uint = 1; i <= 5; i++) { var referenciaBola:Class = getDefinitionByName("Bola" + i.toString()) as Class; var bola:Bola1 = new referenciaBola() as Bola1; bolas.push(bola); addChild(bola); }
Concatemos nossa variável i do loop com a string “Bola”, formando de Bola1 a Bola5. Porém, temos que observar que o tipo da variável bola continua Bola1, o que é errado, pois essa variável assumirá o papel da Bola2, Bola3, etc. Para resolvermos isso, podemos simplesmente trocar de Bola1 para Sprite em ambas situações (do tipo e do cast da função), pois todas nossas bolas têm em comum a superclasse Sprite. Você poderia usar uma interface ou uma classe abstrata nesse caso.
for (var i:uint = 1; i <= 5; i++) { var referenciaBola:Class = getDefinitionByName("Bola" + i.toString()) as Class; var bola:Sprite = new referenciaBola() as Sprite; bola.x = Math.random() * stage.stageWidth; bola.y = Math.random() * stage.stageHeight; bolas.push(bola); addChild(bola); }
Compile e observe que a AVM diz que a variável Bola1 não foi encontrada. Como tiramos todas referências estáticas de nossas classes, o compilador não está compilando-as. Com isso, temos que criar 5 variáveis na nossa classe tipando-as, apenas para fins de compilação:
private var b1:Bola1; private var b2:Bola2; private var b3:Bola3; private var b4:Bola4; private var b5:Bola5;
Podemos compilar agora.
Informações relevantes
Ao criar a instância, podemos passar parâmetros no construtor, dessa forma:
var referenciaBola:Class = getDefinitionByName("Bola" + i.toString()) as Class; var bola:Sprite = new referenciaBola("parametro1", "parametro2", 2009, new Date()) as Sprite;
E nas classes, tratar esses parâmetros.
No exemplo, todas as classes estavam no mesmo pacote que nossa Main.as compilada. Caso suas classes estejam em outros pacotes, é necessário informar no método getDefinitionByName();
var referenciaBola:Class = getDefinitionByName("meu.pacote.Bola" + i.toString()) as Class; var bola:Sprite = new referenciaBola() as Sprite;
Isso por que sua classe se chama meu.pacote.Bola e não apenas Bola.
Baixe os arquivos aqui.
Filed under: action script 3
September 29th, 2009 at 8:41 pm
Muito boa a explicação Vendra!
Este erro de declarar as variáveis, para elas serem encontradas me deu muito trabalho, antes de descobrir que era este o problema hehe.
Para quem está tendo este problema, a mensagem do erro em questão era:
Variable XXX is not defined. Erro 1065.
Continue postando! Abrs!