O'Reilly logo

Essential ActionScript 3.0 by Colin Moock

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Instance Methods

An instance method is a discrete set of instructions that carry out some task related to a given object. Conceptually, instance methods define the things an object can do. For example, the built-in Sound class (whose instances represent sounds in a program) defines an instance method named play that can start a sound playing. Likewise, the built-in TextField class (whose instances represent onscreen text) defines a method named setSelection that can change the amount of text selected in the text field.

To create an instance method, we use a function definition within a class block, as shown in the following generalized code:

class SomeClass {
  function identifier () {
  }
}

In the preceding code, the keyword function begins the instance method. Next comes the instance method name, which can be any legal identifier. (Recall that identifiers must not contain spaces or dashes, and cannot start with a number.) The method name is followed by a pair of parentheses that contain a list of method parameters, which we'll study later. The curly braces ({}) following the parameter list are a block statement. A instance method's block statement is known as the method body. The method body contains directives that perform some task.

Tip

Instance methods are created using the function keyword because they are, technically speaking, a type of function. We'll study functions in Chapter 5.

To execute the code in a given method body, we use a call expression, as shown in the following generalized code. Notice the important and mandatory use of the parentheses operator, ( ), following the method name.

object.methodName()

In the preceding code, methodName is the name of the method whose code should be executed, and object is a reference to the specific instance that will conceptually perform the task represented by the specified method. Using a call expression to execute the code in an instance method's body is known as calling a method of an object (or, synonymously calling a method through an object, or calling an object's method). The term invoke is also used to mean call.

Tip

When discussing a particular method by name, most documentation includes the parentheses operator, ( ). For example, typical documentation would write setSelection( ) rather than setSelection. The convention of including the parentheses operator helps distinguish method names from variable names in prose. To further emphasize the distinction between variable names and method names, this book italicizes method names and uses constant-width font for variable names.

Let's put the preceding concepts into practice in our virtual zoo program.

To give our pets the ability to eat, we'll add a new instance variable and a new instance method to the VirtualPet class. The new instance variable, currentCalories, will track the amount of food each pet has eaten, as a numeric value. The new instance method, eat( ), will implement the concept of eating by adding 100 calories to currentCalories. Eventually, the eat( ) method will be called in response to a user action—feeding a pet.

The following code shows the currentCalories variable definition. To prevent external code from tampering with the amount of calories each VirtualPet instance has, we define currentCalories as private. Notice that each new VirtualPet instance is given 1,000 calories to start:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }
  }
}

The following code shows the basic eat( ) method definition. Notice that, by convention, instance methods are listed after the class's constructor method, while instance variables are listed before the class's constructor method.

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    function eat () {
    }
  }
}

Even though the eat( ) method body does not yet contain any code, with the preceding definition in place, we can already invoke the eat( ) method on a VirtualPet object, as shown in the following updated version of the VirtualZoo class:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      // Invoke eat() on the VirtualPet object referenced by the
      // variable pet
      this.pet.eat();
    }
  }
}

Within the eat( ) method body, we want to add 100 to the currentCalories variable of the object through which the eat( ) method was called. To refer to that object, we use the keyword this.

Tip

Within the body of an instance method, the object through which the method is called is known as the current object. To refer to the current object, we use the keyword this. Notice that the term "current object" can refer to either the object being created in a constructor method or the object through which an instance method was called.

Adding a numeric value (such as 100) to an existing variable (such as currentCalories) is a two-step process. First, we calculate the sum of the variable and the numeric value; then we assign that sum to the variable. Here's the generalized code:

someVariable = someVariable + numericValue

In the case of the eat( ) method, we want to add 100 to the currentCalories variable of the current object (this). Hence, the code is:

this.currentCalories = this.currentCalories + 100;

As a convenient alternative to the preceding code, ActionScript offers the addition assignment operator, +=, which, when used with numbers, adds the value on the right to the variable on the left, as shown in the following code:

this.currentCalories += 100;

Here's the code as it appears in the VirtualPet class:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    function eat () {
      this.currentCalories += 100;
    }
  }
}

From now on, every time a VirtualPet instance's eat( ) method is called, that instance's currentCalories variable will increase by 100. For example, the following code, repeated from the VirtualZoo constructor, increases pet's currentCalories to 1,100 (because all VirtualPet instances start with 1,000 calories).

this.pet = new VirtualPet("Stan");
this.pet.eat();

Notice that even though the VirtualPet characteristic currentCalories is kept private, it can still be modified as result of a VirtualPet instance performing a behavior (eating) that is instigated by an external code. In some cases, however, even instance methods must be kept private. As with instance variables, we use access-control modifiers to control the accessibility of instance methods in a program.

Access Control Modifiers for Instance Methods

The access-control modifiers available for instance method definitions are identical to those available for instance variables—public, internal, protected, and private. An instance method declared public can be accessed both inside and outside of the package in which it is defined; an instance method declared internal can be accessed only inside the package in which it is defined. An instance method declared protected can be accessed by code in the class that contains the method's definition, or by code in descendants of that class only (we haven't studied inheritance yet, so if you are new to object-oriented programming, you can simply ignore protected for now). An instance method declared private can be accessed by code in the class that contains the method's definition only. When no modifier is specified, internal (package-wide access) is used.

By adding access-control modifiers to the methods of the class, we can put the "black box" principle into strict practice. In object-oriented programming, each object can be thought of as a black box that is controlled by an external assortment of metaphoric knobs. The object's internal operations are unknown (and unimportant) to the person using those knobs; all that matters is that the object performs the desired action. An object's public instance methods are the knobs by which any programmer can tell that object to perform some operation. An object's non-public methods perform other internal operations. Hence, the only methods a class should make publicly accessible are those that external code needs when instructing instances of that class to do something. Methods needed to carry out internal operations should be defined as private, protected, or internal. As an analogy, think of an object as a car, whose driver is the programmer using the object, and whose manufacturer is the programmer that created the object's class. To drive the car, the driver doesn't need to know how a car's engine works. The driver simply uses the gas pedal to accelerate and the steering wheel to turn. Accelerating the car in response to the driver stepping on the gas pedal is the manufacturer's concern, not the driver's.

As you manufacture your own classes, focus as much energy designing the way the class is used as you do implementing how it works internally. Remember to put yourself in the "driver's seat" regularly. Ideally, the way the class's public methods are used externally should change very little or not at all each time you make an internal change to the class. If you put a new engine in the car, the driver should still be able to use the gas pedal. As much as possible, keep the volatility of your classes behind the scenes, in private methods.

Tip

In object-oriented terms, a class's public instance methods and public instance variables are, together, sometimes called the class's interface to the outside world—or, synonymously, the class's API (Application Programming Interface).

The term API also refers to the collective services provided by an entire group of classes. For example, the built-in Flash-runtime classes for displaying content on screen are known as the display API. Likewise, a custom set of classes used to render 3D content might be known as a "3D API". In addition to classes, APIs can also include other program definitions (such as variables and functions).

In ActionScript, the term interface has an additional technical meaning, covered in Chapter 9. To avoid confusion, this book does not use the term "interface" to describe an object's public instance methods and public instance variables.

Returning to our virtual zoo program, let's now add an access-control modifier to the VirtualPet class's eat( ) method. We'll make eat( ) a public method because it is one of the official means by which external code is intended to control VirtualPet objects. Here's the revised code:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    public function eat () {
      this.currentCalories += 100;
    }
  }
}

As it stands, the VirtualPet class's eat( ) method is inflexible because it adds the same amount of calories to currentCalories every time it is called. Eventually, we'll want to dynamically adjust the amount of calories added when a pet eats based on the type of food fed to it by the user. To allow the amount of calories added at feeding time to be specified externally when eat( ) is called, we need method parameters.

Method Parameters and Arguments

Like constructor parameters, a method parameter is special type of local variable that is created as part of a method definition, but whose initial value can be (or, in some cases, must be) supplied externally when the method is called.

To define a method parameter, we use the following generalized code. Notice that a method-parameter definition has the same general structure as a constructor-parameter definition.

function methodName (identifier1 = value1,
                     identifier2 = value2,
                     ...
                     identifiern = valuen) {
}

In the preceding code, identifier1=value1,identifier2=value2,...identifiern=valuen is a list of method parameter names and their corresponding initial values. By default, a method parameter's initial value is the value supplied in that parameter's definition. However, a method parameter's value can alternatively be supplied via a call expression, as shown in the following generalized code:

theMethod(value1, value2,...valuen)

In the preceding code, theMethod is a reference to the method being invoked, and value1, value2,...valuen is a list of values that are assigned, in order, to theMethod's parameters. A value supplied to a method parameter through a call expression (as shown in the preceding code) is known as a method argument. Using a method argument to supply the value of a method parameter is known as passing that value to the method.

As with constructor parameters, when a method parameter definition does not include a variable initializer, that parameter's initial value must be supplied via a method argument. Such a parameter is known as a required method parameter. The following generalized code shows how to create a method with a single required method parameter (notice that the parameter definition does not include a variable initializer):

function methodName (requiredParameter) {
}

Any code that calls the preceding method must supply requiredParameter's value using a method argument, as shown in the following generalized code:

theMethod(value)

Failure to supply a method argument for a required parameter causes an error either when the program is compiled (if the program is compiled in strict mode) or when the program runs (if the program is compiled in standard mode).

Now let's update the VirtualPet class's eat( ) method to include a required parameter, numberOfCalories. Each time eat( ) is called, we'll increase the value of the current object's currentCalories variable by the value of numberOfCalories. Here's the updated code for the eat( ) method:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;

    public function VirtualPet (name) {
      this.petName = name;
    }

    public function eat (numberOfCalories) {
      this.currentCalories += numberOfCalories;
    }
  }
}

Because numberOfCalories is a required parameter, its initial value must be supplied externally when eat( ) is called. Let's try it out with the VirtualPet object created in the VirtualZoo constructor. Previously, the code for the VirtualZoo constructor looked like this:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      this.pet.eat();
    }
  }
}

Here's the updated version, which passes the value 50 to eat( ):

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      this.pet.eat(50);
    }
  }
}

The preceding call expression causes eat( ) to run with the value 50 assigned to the numberOfCalories parameter. As a result, 50 is added to the currentCalories instance variable of the VirtualPet instance referenced by pet. After the code completes, the value of pet's currentCalories variable is 1050.

Method Return Values

Just as methods can accept values in the form of arguments, methods can also produce or return values. To return a value from a method, we use a return statement, as shown in the following general code:

function methodName () {
  return value;
}

Tip

The value returned by a method is known as the method's return value or result.

When a method executes, its return value becomes the value of the call expression that called it.

To demonstrate the use of method return values, let's add a new method to the VirtualPet class that calculates and then returns the age of a pet. In order to be able to calculate a pet's age, we need a little knowledge of the Date class, whose instances represent specific points in time. To create a new Date instance, we use the following code:

new Date()

Times represented by Date instances are expressed as the "number of milliseconds before or after midnight of January 1, 1970." For example, the time "one second after midnight January 1, 1970" is expressed by the number 1000. Likewise, the time "midnight January 2, 1970" is expressed by the number 86400000 (one day is 1000 milliseconds × 60 seconds × 60 minutes × 24 hours). By default, a new Date object represents the current time on the local system.

To access a given Date instance's numeric "milliseconds-from-1970" value, we use the instance variable time. For example, the following code creates a new Date instance and then retrieves the value of its time variable:

new Date().time;

On January 24, 2007, at 5:20 p.m., the preceding code yielded the value: 1169677183875, which is the precise number of milliseconds between midnight January 1, 1970 and 5:20 p.m. on January 24, 2007.

Now let's return to the VirtualPet class. To be able to calculate the age of VirtualPet objects, we must record the current time when each VirtualPet object is created. To record each VirtualPet object's creation time, we create an instance of the built-in Date class within the VirtualPet constructor, and then assign that instance to a VirtualPet instance variable, creationTime. Here's the code:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;
    private var creationTime;

    public function VirtualPet (name) {
      this.creationTime = new Date();
      this.petName = name;
    }

    public function eat (numberOfCalories) {
      this.currentCalories += numberOfCalories;
    }
  }
}

Using creationTime, we can calculate any VirtualPet object's age by subtracting the object's creation time from the current time. We'll perform that calculation in a new method named getAge( ). Here's the code:

public function getAge () {
  var currentTime = new Date();
  var age = currentTime.time - this.creationTime.time;
}

To return the calculated age, we use the following return statement:

public function getAge () {
  var currentTime = new Date();
  var age = currentTime.time - this.creationTime.time;

  return age;
}

The following code shows the getAge( ) method in the context of the VirtualPet class:

package zoo {
  internal class VirtualPet {
    internal var petName;
    private var currentCalories = 1000;
    private var creationTime;

    public function VirtualPet (name) {
      this.creationTime = new Date();
      this.petName = name;
    }

    public function eat (numberOfCalories) {
      this.currentCalories += numberOfCalories;
    }

    public function getAge () {
      var currentTime = new Date();
      var age = currentTime.time - this.creationTime.time;
      return age;
    }
  }
}

Now let's use getAge( )'s return value in the VirtualZoo class. Consider the getAge( ) call expression in the following updated version of VirtualZoo:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      this.pet.getAge();
    }
  }
}

In the preceding code, the expression pet.getAge( ) has a numeric value representing the number of milliseconds since the creation of the VirtualPet object referenced by pet. In order to be able to access that value later in the program, we could assign it to a variable, as follows:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      this.pet = new VirtualPet("Stan");
      var age = this.pet.getAge();
    }
  }
}

Alternatively, in a more complete version of the virtual zoo program, we might display the returned age on screen for the user to see.

Method return values are a highly common part of object-oriented programming. We'll use them extensively throughout this book, as you will in your own code.

Note that like any other expression, a call expression can be combined with other expressions using operators. For example, the following code uses the division operator to calculate half the age of a pet:

pet.getAge() / 2

Likewise, the following code creates two VirtualPet objects, adds their ages together, and assigns the sum to a local variable, totalAge:

package zoo {
  public class VirtualZoo {
    private var pet1;
    private var pet2;

    public function VirtualZoo () {
      this.pet1 = new VirtualPet("Sarah");
      this.pet2 = new VirtualPet("Lois");
      var totalAge = this.pet1.getAge() + this.pet2.getAge();
    }
  }
}

Note that when a return statement does not include any value to return, it simply terminates the currently executing method. For example:

public function someMethod () {
  // Code here (before the return statement) will be executed

  return;

  // Code here (after the return statement) will not be executed
}

The value of a call expression that calls a method with no return value (or with no return statement at all) is the special value undefined. Return statements with no return value are typically used to terminate methods based on some condition.

Method Signatures

In documentation and discussions of object-oriented programming, a method's name and parameter list are sometimes referred to as the method's signature. In ActionScript, a method signature also includes each parameter's datatype and the method's return type. Parameter datatypes and method return types are discussed in Chapter 8.

For example, the signature of the eat( ) method is:

eat(numberOfCalories)

The signature of the getAge( ) method is simply:

getAge()

We've now covered the basics of instance methods. Before we conclude this chapter, we'll study one last issue related to ActionScript vocabulary.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required