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

  1. Crie um AS3 Project chamado GetDefinitionByName (FlashDevelop)
  2. 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 | 1 Comment »