« Property Default Value and escaped characters | Main | XPCharting - new features added »

December 03, 2004

Re: Collection & Iterator and Class(cast)

Jesse Warden blogged about Class(cast) today

http://www.jessewarden.com/archives/2004/12/collection_iter.html


I couldnt get the comment posting to work on his site so here is my comment.

On the class casting issue:
I think you maybe are misunderstanding class casting.

Casting NEVER produces a new object instance. It is only ever a check that an object is of a particular class type or extends from a particular class type.

So Button(myobj) doesn't cause myobj to change or construct a new object, it simply confirms that myobj is an instance of Button or is a Button subclass.

With Flash Intrinsic objects like String, Number,Array etc you CANNOT do a cast check. This is because String(myobj) is actually a documented conversion function which takes myobj and constructs a new object instance of a String class.

var val:NewClass = NewClass(o);
Doesn't work in Java or any language I know. The fundemental issue here is a Flash one in that the new Cast syntax conflicts with the older conversion syntax.
Really the problem is that String(myobj) is wrong in doing a conversion and should instead simply do a cast check but that would break backward compatibility I guess. So for one reason or another MM choose to leave it as a bit of a mess where the same syntax means different things depending on the context and leaving some objects not castable.

I blogged about this from the opposite angle a while back
http://www.epresenterplus.com/blog/archives/2004_05.html
(See Casting and Arrays)

Comments

I wouldn't define casting as a "type check" but rather a side-effect of polymorphism. Casting is more of changing how the instance can be used. There is no need to cast to just check if it is a say Person even if it is an instance of Greg. foo instanceOf Person will return true even if foo = new Greg(); (and greg extends person of course) The only places I tend to use casting is when casting to an interface. I didn't read the article that you are talking about but I think that may be what they do in it. This is to confirm that the instance you have been given adheres to the "contract" you hold with it. Developers may forget to implement an interface therefore breaking the agreement, if you did not do this check you may have an error fly by. Casting should be used conservatively just because there are but a few reasons to use it. The other reason to cast is upcasting, that is making Person an instanceof Greg this has its uses. Either way in Java you are always taught to never ever cast unless you have to, it is bad for performance. Now for C its perfectly valid because of pointers. For C++ it is automatic as well just like Flash and Java. Hope all that makes sense.

Forget the upcasting part at the end, that was a thought I had meant to remove. upcasting is dangerous in most languages and they will not let you do it. Flash probably does but its bad practice :).

Hi Greg,
The issue in the blog entry I was reffering to was why String(obj) "worked" as in it produced a new object instance and why Button(obj) didnt work as in it didnt produce a new object.

The point I was trying to emphasize by use of words was a cast doesnt produce a new object or "change" the exisitng object it performs a type check and after that allows you to use the referenced object as the type it was cast to. The common syntax of cast and conversion functions in Flash is very bad in my opinion and leads to this confusion.

As Flash is only psuedo strongly typed you dont really need it to use one type as another so really all you are getting out of is the type check assurance and I doubt if there is any material performance difference between instanceof and castcheck in Flash.

As far as casting in Java, my attitude is only cast when I am forced too by the compiler :-).. however the performance cost is not really bad unless of course its heavily used code, what with HotSpot and all I doubt you would notice an uneeded cast not that I am championing the cause of casting with wild abandon but just I dont lose sleep over it.

Yea you are correct those are annoying I have had many clients get caught by those differences. I think we agree on the subject of casting in general. I am sure the next version of as will fix some of these slight differences...macromedia loves watching blogs for issues like this so that they can fix them.

To be honest, I feel that casting is broken in Flash. I would rather have seen it implemnted (Type)variable so that it doesn't look like a constructor call.

A perfect example is Array(variable_to_be_casted_as_array). It doesn't work as you would expect, and actually creates a new array. This falls under "conversion" functions and is documented as such, but I still feel casting in general is broken.

I wasn't able to comment last night either, but I basically wrote about what the livedocs have to say about this:

The reason Object() and Array() etc return a new instance is because:

Livedocs: "You can't override primitive data types that have a corresponding global conversion function with a cast operator of the same name"

As Blackmamba says, it's only there to assert, not to convert, even tho the assertion fails silently in certain circumstances.

Interesting tho is more player inconsistencies...
When you pass in say an Object instead of a CompactDisc to a function, and cast it as CompactDisc, you get "null" back (meaning the casting has failed in Flash Player 7), not a CompactDisc (even tho Flash Player 6 does still return the Object rather than null)... even if their structure is indeed identical. Basically casting works for explicit subclasses i.e. MyClass can be cast to MySubClass, but, even tho all Classes are a sub-class of object, you can never downcast an Object to a MySubClass of Object or u get null (FP7). :s

For others reading this who are still a little fuzzy on casting, Moock's 'Essential Actionscript 2.0' (pp 45-54) is pretty helpful on this topic... Provides a good number of examples and workarounds.

Page 52 provides the exact issue that Richard mentioned: "In ActionScript2, upcasts and downcasts should only be performed between subclasses and superclasses, despite the fact that the compiler lets you cast to ANY datatype. Again, remember that CONVERTING between two datatypes (e.g., a Number and a Boolean) is legitimate, but casting between such types is not."

Casting up to a superclass is not a problem because your class is a more specialized type of the superclass... However, Mr. Moock mentions that downcasting is allowed in flash and continues that it is unsafe because you aren't guaranteed that all the functions from the subclass will be in the superclass. (p.48)

Got it, makes sense.

So I have a hard time believing that his assertion on page 49 that this:
var ball3:Basketball = Basketball(new Ball());
... is valid(but potentially unsafe), fails in my tests. I always get null back.

From the runtime casting example in the Moock book:
SomeType(someObj) only works if (someObj instanceof SomeType) == true.

This seems like a conflict... Basketball(new Ball()) only works if (new Ball() instanceof Basketball). How is this ever possible?

Wouldn't this mean that runtime downcasting is prohibited in Flash... or am I missing something?

As i understand it, you use casting for being able to work with different classes, that are all familiar in a dynamical way. Let's say, you have a superclass 'component' and lots of real components like 'link', 'text', etc. And let's say, you have an application, where you have a function, that delivers components out of an array. this would be something like:

public function getComponent():Component {...}

So this function is only able to deliver instances of type 'Component'. The Superclass Component has only the main functions, that a component needs, the link component has the special link-related functions, like say setUrl(). So let's say, you call the function getComponent() int his manner:

var myComp:Component = myApplication.getComponent();

Now you could make a typecheck first:

if(Link(myComp) != null) trace ("Yehaaa, a link!");

OK, now that we're sure, we have actually a link, we would like to use the setUrl function. But at the moment the compiler wouldn't allow it, because he would say, that the class 'Component' doesn't have a function called 'setUrl'. But because we know better, we make a typecast:

var myLink:Link = Link(myComp);

So, now the compiler sees, myLink is of type Link, so we can use the function setUrl.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)