Q&A – is, as, and type conversion

A question from a reader gave me an excuse to write a huge rant about type conversions:

I’m doing a little exercise in a book that makes a textfield in which each letter can only be entered once. Not very useful, more of a teaching thing really. However there’s a bit of code that says:

var tf:TextField = event.target as TextField;

I don’t understand this at all! What the hell is event.target as TextField? Anyway here’s the full code for context’s sake:

package com.FoundationAS3.ch6 {

	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.events.TextEvent;

	public class PreventDefaultTest extends Sprite {

		public function PreventDefaultTest() {
			var tf:TextField = new TextField();
			addChild(tf);

			tf.width = stage.stageWidth;
			tf.height = stage.stageHeight;
			tf.type = TextFieldType.INPUT;
			tf.wordWrap = true;

			tf.addEventListener(TextEvent.TEXT_INPUT, onTextFieldTextInput);
		}

		private function onTextFieldTextInput(event:TextEvent):void {
			var tf:TextField = event.target as TextField;
			if (tf.text.indexOf(event.text) > -1) {
				event.preventDefault();
			}
		}
	}
}

Thanks,
-Neal

My response after the jump.

Neal,

First of all, let me say that preventing the default event handler, e.g. for a TextField, is rarely used and isn’t worth spending a lot of time discussing.

On the other hand, the as operator (along with the is operator and the type cast) are totally useful and you should learn all about them. First, let me cover is:

is

is is an operator that evaluates whether some object is of a certain type. The syntax is [object] is [type]. The term ‘type’ is similar but not interchangeable with ‘class’. A type can refer to not only the class of an object, but also the super-classes it extends and interfaces it implements. An example:

var sprite:Sprite = new Sprite();
trace(sprite is Sprite); // true, because the sprite object is an instance of Sprite
trace(sprite is DisplayObject); // true, Sprite is a subclass of DisplayObject
trace(sprite is MovieClip); // false, MovieClip is a subclass of Sprite.
trace(sprite is IEventDispatcher); // true, DisplayObject extends EventDispatcher which implements IEventDispatcher.

Note that sprite is not a MovieClip but a MovieClip is a Sprite. Because MovieClip extends Sprite and contains all the same methods and properties as Sprite but not the other way around.

Note: the instanceof operator commonly used in AS1/2 still exists in AS3 but is for all practical purposes deprecated by is (although still useful if you’re manipulating prototypes). typeof is also related but shouldn’t be used in place of is because it only returns strings for primitive data types like Object and String. In order to get the actual name of the class that an object is an instance of, you can use the flash.utils.getQualifiedClassName() global function.

as

as functions similarly to is except that it actually will attempt to coerce the object into the data type that you ask for. Coercion, AKA type conversion or casting, is the act of changing the data type of an object.

With the case from your book, an Event object has a target property which refers back to the object that originally dispatched the event. Since the Event object is designed to be sent out from all different types of objects, the target’s data type is Object (the mother of all the other classes). So, if the variable you’re defining is a TextField, you’re expecting an object with a text property. Object, doesn’t have a text property so you have a dilemma. The solution is to cast the object into the type you want it to be, basically forcing it to be more specific.

var textField:TextField = event.target; // Throws compile error because target is an Object not a TextField

var textField:TextField = event.target as TextField; // OK! as long as the event target actually is a text field

Going from a more generic type to a more specific type is called a downcast. Going from a more specific type to a more generic type is called an upcast. Upcasting in ActionScript, as with most other languages, is done automatically and is always safe. e.g. a Sprite can be used as a parameter for a function that requires a DisplayObject. Downcasting on the other hand is unsafe meaning that you can’t always guarantee that it will work. A DisplayObject is not necessarily a Sprite.

When an attempt to downcast with as fails, the value returned will be null:

var string:String = new String();
var textField:TextField = string as TextField // string can't be converted. textField will be set to null

If you’re not sure whether your object is the right type (and you should never assume it is) check to see if the value is null before proceeding:

var typedObject:TypedObject = mysteryObject as TypedObject;
if (typedObject != null) {
	// conversion was successful! do something with typedObject
} else {
	// conversion failed
}

Type casts

Another way to do this with slightly different results is by type casting. Type casting works with the syntax Type(object) and is functionally similar to object as Type. The only difference is with a type cast, you get a runtime error if the coercion fails.

var textField:TextField = TextField(event.target); // if event.target is a TextField, you're good. If not, you get an error.

you can also shortcut like this:

TextField(event.target).text = "foo";

Conclusion

Why are objects like event.target given such generic data types? The idea is that you want to minimize the requirements of a property, parameter, or function to maximize the number of objects that will qualify thus making your program more flexible. In other words, strive to use the most broadly defined data types that you can get away with.

For example, say you’re creating a simulator program for getting a person from their home to their job. You may be tempted to define your modeOfTransportation variable with a type of Car. But down the road the company you’re working for wants to create a more green image for themselves and suggests using alternative modes of transportation. If you’re code is based on Car, it’s limited in what you can do with it. However, if you had used a Vehicle type (includes Car, Bike, Motorcycle, Donkey) instead, you may not have had a problem. Using interfaces like IConveyance makes your program even more flexible because you can include non-vehicluar objects like MolecularTransporter, Feet, and CrowdSurfing as long as they satisfy the interface. In fact, I would suggest as an experiment that the next time you start a project, try coding all the interfaces first before you move on to coding specific classes. You’ll be surprised at how flexible it is and how it forces you to consider the roles of your classes from a higher level.

Though you should strive for generic types, sometimes you will need to get specific. If your program has a gas station simulator in it, the fillTankOfVehicle() method might require a MotorVehicle class (includes Car, Motorcycle, Plane) instead of simply Vehicle. Still, you should only get very specific when your program requires it. If you are making a racing program, use F1RaceCar only when a simple Car won’t cut it.

I hope this helps! This is definitely a difficult topic even for experienced programmers to grasp!

About Mims H Wright

http://dispatchevent.org/wp-content/avatars/animemims.gif
This entry was posted in AS3, Architecture, Programming, Tips, Tricks, and Hacks and tagged , , , , , , , , . Bookmark the permalink.

4 Responses to Q&A – is, as, and type conversion

  1. Dan says:

    Hey, should add that Type(object) in some cases actually converts the object, like with XML/XMLList.. one of the slightly arcane aspects of AS3.

    Cheers, Dan

  2. It’s also worth noting that the syntax for type casting is an overloaded syntax, which makes it ambiguous. In some cases it casts the type, in others it attempts a type conversion.

    TextField(myvar) // casts
    String(myvar) // converts

    I use “as” for all casting, and only use the above syntax for conversion. This keeps the intent clear.

  3. Dan & Grant, Good points. I should have mentioned that. Using the type cast can actually convert an object into the other class even if they are not the same type. In fact, Array(obj) should always be replaced with ‘as’ because it’s the equivalent of using new Array(obj).
    I would also recommend always using as for type casting.

  4. adehaas says:

    Thanks for that, nice refresher on this topic!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Spam Protection by WP-SpamFree