Custom Classes: Chapter 2 - ActionScript 3.0 Cookbook
Pages: 1, 2

Section 2.5: Creating Subclasses

Problem

You want to create a class that inherits from an existing class.

Solution

Write a subclass using the extends keyword.

Discussion

There are cases when a new class is a more specific version of an existing class. The new class may feature much of the same behavior as the existing class. Rather than rewriting all the common functionality you can define the new class so it inherits all the functionality of the existing class. In relation to one another, the new class is then called a subclass and the existing class is called a superclass.

You can define inheritance between classes in the subclass declaration using the extends keyword, as follows:

public class Subclass extends Superclass
            

A subclass can reference any public or protected properties and methods of the superclass. private properties and methods are not accessible outside the class, not even to a subclass.

Inheritance is a powerful technique; however, as with anything else, it is important that you use inheritance correctly. Before writing a subclass you need to determine whether or not the new class actually has a subclass relationship with the existing class. There are two basic types of relationships that classes can have: inheritance and composition. You can usually quickly determine the correct relationship between classes by asking whether it's an "is a" relationship or a "has a" relationship:

  • "Is a" relationships are often inheritance relationships. As an example, consider an application that manages a library's collection.
  • "Has a" relationships are composition relationships in which a class declares a property. Most classes use composition. Oftentimes composition can be implemented in such a way that it achieves the same results as inheritance with greater flexibility (yet generally requiring more code). For example, a book is not an author, but it has an author (or authors).

The library has different types of items in the collection including books and DVDs. Obviously books and DVDs have different types of data associated with them. Books have page counts and authors, while DVDs might have running times, actors, directors, etc. However, you also want to associate certain common types of data with both books and DVDs. For example, all library items might have Dewey decimal classifications as well as unique identification numbers assigned by the library. And every sort of library item has a title or name. In such a case, it can be advantageous to define a class that generalizes the commonality of all library items:

package org.examplelibrary.collection {
    public class LibraryItem {
        protected var _ddc:String;
        protected var _id:String;
        protected var _name:String;

        public function LibraryItem( ) {}

        public function setDdc(value:String):void {
            _ddc = value;
        }
        public function getDdc( ):String {
            return _ddc;
        }

        public function setId(value:String):void {
            _id = value;
        }
        public function getId( ):String {
            return _id;
        }

        public function setName(value:String):void {
            _name = value;
        }
        public function getName( ):String {
            return _name;
        }
    }
}

Then you can say that books and DVDs are both types of LibraryItem. It would then be appropriate to define a Book class and a DVD class that are subclasses of LibraryItem. The Book class might look like the following:

package org.examplelibrary.collection {
    import org.examplelibrary.collection.LibraryItem;
    public class Book extends LibraryItem {
        private var _authors:Array;
        private var _pageCount:uint;

        public function Book( ) {}

        public function setAuthors(value:Array):void {
            _authors = value;
        }
        public function getAuthors( ):Array {
            return _authors;
        }

        public function setPageCount(value:uint):void {
            _pageCount = value;
        }
        public function getPageCount( ):uint {
            return _pageCount;
        }
    }
}

The "Is a" and "Has a" test is helpful, but not always definitive in determining the relationship between classes. Often composition can be used even when inheritance would be acceptable and appropriate. In such cases the developer might opt for composition because it offers an advantage or flexibility not provided by inheritance. Furthermore, there are times when a class may appear to pass the "Is a" test yet inheritance would not be the correct relationship. For example, the library application might allow users to have accounts, and to represent the user, you would define a User class. The application might differentiate between types of users; for example, administrator and standard users. You could define Administrator and StandardUser classes. In such a case, the classes would appear to pass the "Is a" test in relation to User. It would seem to make sense that an Administrator is a User. However, if you consider the context an Administrator isn't actually a User, but more appropriately an Administrator is a role for a User. If possible, it would be better to define User so it has a role of type Administrator or StandardUser.

By default it's possible to extend any class. However you may want to ensure that certain classes are never subclassed. For this reason you can add the final attribute to the class declaration, as follows:

final public class Example

Section 2.6: Implementing Subclass Versions of Superclass Methods

Problem

You want to implement a method in a subclass differently than how it was implemented in the superclass.

Solution

The superclass method must be declared as public or protected. Use the override attribute when declaring the subclass implementation.

Discussion

Often a subclass inherits all superclass methods directly without making any changes to the implementations. In those cases, the method is not redeclared in the subclass. However, there are cases in which a subclass implements a method differently than the superclass. When that occurs, you must override the method. To do that, the method must be declared as public or protected in the superclass. You can then declare the method in the subclass using the override attribute. As an example, you'll first define a class, Superclass:

package {
    public class Superclass {
        public function Superclass( ) {}
        public function toString( ):String {
            return "Superclass.toString( )";
        }
    }
}

Next, define Subclass so it inherits from Superclass:

package {
    public class Subclass extends Superclass {
        public function Subclass( ) {}
    }
}

By default, Subclass inherits the toString( ) method as it's implemented in Superclass:

var example:Subclass = new Subclass( );
trace(example.toString( )); // Displays: Superclass.toString( )

If you want the toString( ) method of Subclass to return a different value, you'll need to override it in the subclass, as follows:

package {
    public class Subclass extends Superclass {
        public function Subclass( ) {}
        override public function toString( ):String {
            return "Subclass.toString( )";
        }
    }
}

When overriding a method, it must have exactly the same signature as the superclass. That means the number and type of parameters and the return type of the subclass override must be exactly the same as the superclass. If they aren't identical, the compiler throws an error.

Sometimes when you override a method you want the subclass implementation to be entirely different from the superclass implementation. However, sometimes you simply want to add to the superclass implementation. In such cases, you can call the superclass implementation from the subclass implementation using the super keyword to reference the superclass:

super.methodName( );

See Also

Recipe 2.5

Section 2.7: Creating Constants

Problem

You want to declare a constant.

Solution

Declare it just like you would declare a property, except use the const keyword in place of var.

Discussion

As the name constant implies, constant values do not change. Constants are useful when you have complex values that you want to be able to reference by a simple identifier or when you want to be able to use compile-time error checking for values. Math.PI is an example of a constant that contains a complex value (which is the value of pi, or 3.14159). MouseEvent.MOUSE_UP, which contains the value mouseUp, is an example of a constant that allows you to use error-checking. When you add an event listener for the mouse up event, you can use the string value mouseUp. However, if you accidentally have a typo, you won't be notified of an error, and your code won't work as expected:

// This is valid code, but because of the typo (mousUp instead of mouseUp) the 
// code won't work as expected.
addEventListener("mousUp", onMouseUp);

Using a constant helps. If you accidentally misspell the constant, you will receive a compile error that helps you track down the error:

// This causes a compile error.
addEventListener(MouseEvent.MOUS_UP, onMouseUp);

The syntax for declaring a constant is very similar to that for declaring a standard property. However, rather than using the var keyword you use the const keyword. Although not required, the majority of constants also happen to be public and static. If you want a constant to be public and static, you must use the correct attributes. Additionally, you must assign a value for a constant when declaring it:

static public const EXAMPLE:String = "example";

By convention, constant names are all in uppercase. This convention makes it easy to identify and differentiate constants from properties.

See Also

Recipe 2.4

Section 2.8: Dispatching Events

Problem

You want to dispatch events.

Solution

Extend flash.events.EventDispatcher and call the dispatchEvent( ) method.

Discussion

Events are an important way for objects to communicate. They are essential for creating flexible systems. Flash Player 9, for example, has a built-in event dispatching mechanism in the flash.events.EventDispatcher class. All classes that dispatch events inherit from EventDispatcher (e.g., NetStream and Sprite). If you want to define a class that dispatches events, you can extend EventDispatcher, as follows:

package {
    import flash.events.EventDispatcher;
    public class Example extends EventDispatcher {

    }
}

The EventDispatcher class has public methods called addEventListener( ) and removeEventListener( ) that you can call from any instance of an EventDispatcher subclass to register event listeners. EventDispatcher also defines a protected method called dispatchEvent( ), which you can call from within a subclass to dispatch an event. The dispatchEvent( ) method requires at least one parameter as a flash.events.Event object or a subclass of Event.

This excerpt is from ActionScript 3.0 Cookbook. Well before Ajax and Windows Presentation Foundation, Macromedia Flash provided the first method for building "rich" web pages. Now, Adobe is making Flash a full-fledged development environment, and learning ActionScript 3.0 is key. That's a challenge for even the most experienced Flash developer. This Cookbook offers more than 300 solutions to solve a wide range of coding dilemmas, so you can learn to work with the new version right away.

buy button