(请注意,我的代码(从某种意义上说它有效)并没有什么“错误”,但更多的是想知道它是如何工作的以及在幕后发生的事情) < / p>
目前我有两个库,每个库都有一个对象。一个设置为“Apple”类,另一个设置为“Pear”,它们位于单独的外部swfs中。
Apple的基类是MovieClip,因为内容方面它是一个movieclip:有帧+动画
以下是我用来创建和显示Apple movieclip对象的代码:
function getClip(inputName, spriteLibrary:Loader):MovieClip {
var aClass:Class = spriteLibrary.contentLoaderInfo.applicationDomain.getDefinition(inputName) as Class;
return (MovieClip) (new aClass());
}
this.addChild(getClip("Apple", referenceToTheLoadedSwfThatHasAppleInIt));
上述工作正常,Apple出现在舞台上并播放。
然而,Pear的基类是Sprite(没有动画,帧等)。所以上面的失败,因为该方法应该返回一个MovieClip。
this.addChild(getClip("Pear", referenceToTheLoadedSwfThatHasPearInIt));
我想了一会儿我必须有两个版本的上述方法,一个用于Sprite,一个用于MovieClip。但只是看,我把它改成了Sprite并试图创建Apple:
function getClip(inputName, spriteLibrary:Loader):Sprite {
var aClass:Class = spriteLibrary.contentLoaderInfo.applicationDomain.getDefinition(inputName) as Class;
return (Sprite) (new aClass());
}
this.addChild(getClip("Apple"), referenceToTheLoadedSwfThatHasAppleInIt);
this.addChild(getClip("Pear"), referenceToTheLoadedSwfThatHasPearInIt);
现在两者都有效,但有趣的是,我发现即使该方法返回一个Sprite,Apple仍然可以正常工作并在舞台上播放它的动画。我可以将它转换为MovieClip并访问所有与MovieClip相关的属性,等等。
我的问题是,当Apple作为Sprite“存在”时,所有与MovieClip相关的“东西”都发生了什么事情,这在使用MovieClips和Sprites时实际上是正常的事情(假装你只是只有在需要的时候才有精灵并投射到MovieClip?)
答案 0 :(得分:2)
将一个MovieClip强制转换为Sprite不会剥夺它的MovieClip实现;它只是告诉调用代码(在这种情况下,this.addChild()
)“这个对象是一个精灵;请将其视为这样。”调用代码不知道 - 或者不关心 - 它实际上是一个MovieClip,因为只要它是一个DisplayObject(一个Sprite,又是一个MovieClip,派生自),addChild()
方法会很乐意接受它
由于MovieClip 无论如何都是一个Sprite,它对调用代码没有任何影响。至于Apple对象本身,它仍然是一个内心的MovieClip,因此将继续像它自己一样运行。
作为一个类比,想想去杂货店买一些商品,然后在柜台付款。你是一个在生活的各个方面有许多不同角色的人,但就收银员而言,你只是一个顾客进行购买。这并不能使你成为一个人,但收银员不必对你是谁或你做什么感兴趣,除了购买顾客之外。
答案 1 :(得分:2)
Sprite
类提供比MovieClip
更多的基本功能,但可以使用Sprite
类的功能操作MovieClip的所有内容。实际上,如果您的唯一目的是执行DisplayObject
,则您的方法可以返回与addChild()
一样低的类。类型转换不会剥离任何功能的类型转换对象,而是限制对其属性的可用调用。比如,DisplayObject
具有x
和y
属性,可以使用Sprite
向自身添加对象(addChild()
方法){{1 }}没有,DisplayObject
有内部动画和MovieClip
没有的gotoAndStop()
方法。因此,如果您将Sprite
类型转换为Apple
,则无法拨打参考Sprite
,因为您已告知程序该参考是只是gotoAndStop()
。如果您将Sprite
或Apple
对象强制转换为Pear
,则无法调用其DisplayObject
方法来添加健康栏(对于拥有健康栏的苹果来说很奇怪,但为什么不呢?),因为引用不知道底层对象支持这个功能。但实际上任何一个对象都不会发生任何事情,无论你如何对它们进行类型转换,你都会限制自己通过接收引用来应用更高级的操作。
实际上,通过类型转换来限制自己的功能是一种很好的做法,因为您可以免受制作&#34;拐杖&#34;工作代码可能会破坏其目的。比如说,如果您决定将addChild()
转换为具有定制属性,嵌入式事件监听器,颜色更改的高级Apple
类(例如,Sprite
),您的代码就不会感到惊讶,但是如果你将新创建的public class Apple extends Sprite {...}
类型转换为Apple
或Sprite
,那么你将从实例的任何额外功能中抽象出来并仅使用其基本接口(类型化类的属性和方法) )执行DisplayObject
或Sprite
旨在执行的操作。如果在高级DisplayObject
中有一些被覆盖的属性,它们将在制作Apple
类时按预期工作,即使从超类描述中解决 - 这实际上是覆盖的目的。
简而言之,不要担心失去功能,但尝试使用已使用的功能进行类型转换为最少的类。
关于&#34;幕后发生的事情&#34;:每个类都有自己的属性和方法表,如果一个类正在扩展另一个类,那么该类的表与超类#39相同;直到最后,额外的空间占用了类所实现的属性和方法的信息。如果有覆盖,则重写方法的信息将替换表中相应方法的信息,而不是超类。每个实例都有一个分配给它的内存块,有一个对类的属性和方法表的引用,如果通过实例引用调用方法,则该表用于执行正确的代码。因此,当您调用方法或分配了getter或setter的属性时,会发生以下情况:
Apple
属性引用实际上该实例的正确类的类。 AS3中的每个班级都有此属性。该实例通过&#34;此&#34;指针先前被推入堆栈。 (从技术上讲,它不仅仅存储在堆栈中,因此引用与堆栈中的引用相同)答案 2 :(得分:2)
这里要理解的最重要的事情是对象本身没有任何反应,但编译器处理对象的方式会有所不同。
例如,编译器会将此函数返回的对象视为Sprite
,即使我们实际返回MovieClip
:
function makeSprite():Sprite
{
return new MovieClip();
}
var test:MovieClip = makeSprite();
// 1118: Implicit coercion of a value with static type flash.display:Sprite
// to a possibly unrelated type flash.display:MovieClip.
所以我们在这里需要做的是(正如你当前所理解的那样)通过类型转换告诉编译器结果是实际一个MovieClip
:
var test:MovieClip = makeSprite() as MovieClip;
另一件需要注意的事情是,如果您要trace()
结果,那么您将获得[object MovieClip]
而不是[object Sprite]
:
trace( makeSprite() ); // [object MovieClip]
使用is
检查MovieClip
是否会返回true
:
trace( makeSprite() is MovieClip ); // true
即使您为test
使用了更原始的类型,您的对象也会真正成为MovieClip
:
var test:Object = makeSprite();
trace(test); // [object MovieClip]
答案 3 :(得分:1)
TL; DR:
您的MovieClip将保留一个MovieClip,但是下载到Sprite的引用将只能访问Sprite可用的方法和变量。