J'ai 3 classes très simples qui sont les suivantes :

  • Une classe 'A'
  • Une classe 'B' qui hérite de 'A'
  • Une classe 'Ref' qui utilise 'B' et est référencée dans 'A'

Voici le code de chacune :

class Ref
{
	public function createB(Void):A
	{
		return new B();
	}
}
class A
{
	public static function get TEST():Ref { return __ref; }
	private static var __ref:Ref = new Ref();
 
	public function toString(Void):String { return "class A"; }
}
class B extends A { }

Et à l'intérieur de mon flash, le code suivant :

var b:B = new B();
trace(b); //class A

Tout semble fonctionner parfaitement. Maintenant, pour des besoin d'abstraction, je modifie le type de la variable b dans mon flash :

var b:A = new B();
trace(b);

A première vue, il ne semble pas y avoir de problème puisque B hérite de A. On peut donc s'attendre à ce que le trace nous sorte la même chose que le code précédent ('class A' qui est renvoyé par toString()). Et pourtant...

[object Object]

Que se passe-t-il ??? Approfondissement avec quelques trace supplémentaires :

var b:A = new B();
trace("b : "+b);
trace("b instanceof A : "+(b instanceof A));
trace("b instanceof B : "+(b instanceof B));
trace("A __proto__ : "+A.prototype.__proto__);
trace("B __proto__ : "+B.prototype.__proto__);

Ce qui donne :

b : [object Object]
b instanceof A : false
b instanceof B : true
A __proto__ : [object Object]
B __proto__ : [object Object]

Visiblement, la classe 'B' n'hérite plus de 'A' ! Et aucun message d'erreur comme quoi il y a eu un souci... En gros, la galère à débugger surtout si on doit chercher dans tout un package de classes qui font références les unes aux autres.

La classe 'B' n'ayant rien à voir la-dedans, la solution ne peut être trouvée que dans la classe 'A'. Et les seuls éléments à tester sont la constante et la variable statique.

class A
{
	//public static function get TEST():Ref { return __ref; }
	private var __ref:Ref = new Ref(); //n'est plus static !
 
	public function toString(Void):String { return "A"; }
}

Avec ce morceau de code, flash proteste à la compilation :

A class's instance variables may only be initialized to compile-time constant expressions.

Ce qui est drôle c'est que si la variable est statique (avec le code ci-dessus), aucun problème à la compilation, mais une exécution complètement erronée comme auparavant. Par contre, l'indication du compilateur pour la variable d'instance nous fournit une solution :

class A
{
	public static function get TEST():Ref { return __ref; }
	private static var __ref:Ref;
 
	public function A(Void)
	{
		if (__ref == null) __ref = new Ref();
	}
 
	public function toString(Void):String { return "A"; }
}

Et tout refonctionne normalement ! C'est vraiment un gros cafouillage de la part de Macrobe* de ne pas envoyer de message d'erreur à la compilation (et c'est pas le seul !). Cette perte d'héritage au runtime est vraiment sournoise et même en forcant l'héritage via __proto__ ca ne (re)marche pas parfaitement...

Bref, un gros pétage de plomb et quelques heures de recherches m'ont finalement aiguillés sur la solution ! Pour ceux qui veulent tester, j'ai mis les sources en annexes du billets.

-> Macrobe : concaténation hasardeuse de Macromedia et Adobe