Chapter 4. Static Variables and Static Methods

In Chapter 1, we learned how to define the characteristics and behavior of an object using instance variables and instance methods. In this chapter, we’ll learn how to manage information and create functionality that pertains to a class, itself, rather than its instances.

Static Variables

Over the past several chapters, we’ve had a fair bit of practice working with instance variables, which are variables associated with a particular instance of a class. Static variables, by contrast, are variables associated with a class itself, rather than a particular instance of that class. Static variables are used to keep track of information that relates logically to an entire class, as opposed to information that varies from instance to instance. For example, a class representing a dialog box might use a static variable to specify the default size for new dialog box instances, or a class representing a car in a racing game might use a static variable to specify the maximum speed of all car instances.

Like instance variables, static variables are created using variable definitions within class definitions, but static variable definitions must also include the static attribute, as shown in the following generalized code:

class SomeClass {
  static var identifier = value;
}

As with instance variables, access-control modifiers can be used to control the accessibility of static variables in a program. The access-control modifiers available for static-variable definitions are identical to those available for instance-variable definitions—public, internal, protected, and private. When no modifier is specified, internal (package-wide access) is used. When a modifier is specified, it is typically placed before the static attribute, as shown in the following code:

class SomeClass {
  private static var identifier = value;
}

To access a static variable, we provide the name of the class that defines the variable, followed by a dot (.), followed by the name of the variable, as shown in the following generalized code:

SomeClass.identifier = value;

Within the class that defines the variable, identifier can also be used on its own (without the leading class name and dot). For example, in a class, A, that defines a static variable v, the expression A.v is identical to the expression v. Nevertheless, to distinguish static variables from instance variables, many developers (and this book) include the leading class name even when it is not strictly required.

Static variables and instance variables of the same name can coexist within a class. If a class, A, defines an instance variable named v, and a static variable, also named v, then the identifier v on its own refers to the instance variable, not the static variable. The static variable can be accessed only by including the leading class name, as in A.v. The instance variable is, therefore, said to shadow the static variable.

Now let’s add some static variables to our VirtualPet class. As we just learned, static variables are used to keep track of information that relates logically to an entire class and does not vary from instance to instance. There are already two such pieces of information in our VirtualPet class: the maximum length of a pet’s name and the maximum number of calories a pet can consume. To track that information, we’ll add two new static variables: maxNameLength and maxCalories. Our variables are not required outside the VirtualPet class, so we’ll define them as private. The following code shows the maxNameLength and maxCalories definitions, with the rest of the VirtualPet class code omitted in the interest of brevity:

package zoo {
  internal class VirtualPet {
    private static var maxNameLength = 20;
    private static var maxCalories = 2000;

    // Remainder of class not shown...
  }
}

With our maxNameLength and maxCalories variables in place, we can now update the getHunger( ), eat( ), and setName( ) methods to use those variables. Example 4-1 shows the latest version of the VirtualPet class, complete with static variables. Changes since the previous version are shown in bold. Notice that, by convention, the class’s static variables are listed before the class’s instance variables.

Example 4-1. The VirtualPet class
package zoo {
  internal class VirtualPet {
    private static var maxNameLength = 20;
    private static var maxCalories = 2000;

    private var petName;
    // Give each pet 50% of the maximum possible calories to start with.
    private var currentCalories = VirtualPet.maxCalories/2;

    public function VirtualPet (name) {
      setName(name);
    }

    public function eat (numberOfCalories) {
      var newCurrentCalories = currentCalories + numberOfCalories;
      if (newCurrentCalories > VirtualPet.maxCalories) {
        currentCalories = VirtualPet.maxCalories;
      } else {
        currentCalories = newCurrentCalories;
      }
    }

    public function getHunger () {
      return currentCalories / VirtualPet.maxCalories;
    }

    public function setName (newName) {
      // If the proposed new name has more than maxNameLength characters...
      if (newName.length > VirtualPet.maxNameLength) {
        // ...truncate it
        newName = newName.substr(0, VirtualPet.maxNameLength);
      } else if (newName == "") {
        // ...otherwise, if the proposed new name is an empty string,
        // then terminate this method without changing petName
        return;
      }

      // Assign the new, validated name to petName
      petName = newName;
    }

    public function getName () {
      return petName;
    }
  }
}

In Example 4-1, notice that the maxNameLength and maxCalories variables help centralize our code. For example, previously, to update the maximum allowed number of characters in a name, we would have had to change the number 20 in two places within the setName method—a process that is both time-consuming and prone to error. Now, to update the maximum allowed number of characters, we simply change the value of maxNameLength, and the entire class updates automatically.

Tip

Unexplained literal values such as the number 20 in the previous version of setName( ) are known as “magic values” because they do something important, but their purpose is not self-evident. Avoid using magic values in your code. In many cases, static variables can be used to keep track of values that would otherwise be “magic.”

Static variables are often used to maintain settings whose values should not change once a program has started. To prevent a variable’s value from changing, we define that variable as a constant, as discussed in the next section.

Constants

A constant is a static variable, instance variable, or local variable with a value that, once initialized, remains fixed for the remainder of the program. To create a constant, we use standard variable-definition syntax, but with the keyword const instead of var. By convention, constants are named with all capital letters. To create a constant static variable, we use the following generalized code directly within a class body:

static const IDENTIFIER = value

To create a constant instance variable, we use the following generalized code directly within a class body:

const IDENTIFIER = value

To create a constant local variable, we use the following generalized code within a method or function:

const IDENTIFIER = value

In the preceding three code examples, IDENTIFIER is the name of the constant, and value is the variable’s initial value. For constant static variables and constant local variables, once value has been assigned by the variable initializer, it can never be reassigned.

For constant instance variables, if the program is compiled in strict mode, once value has been assigned by the variable initializer, it can never be reassigned. If the program is compiled in standard mode, after value has been assigned by the variable initializer, the variable’s value can also be assigned within the constructor function of the class containing the variable definition, but not thereafter. (We’ll learn the difference between strict mode and standard mode compilation in Chapter 7.)

Constants are typically used to create static variables whose fixed values define the options for a particular setting in a program. For example, suppose we’re building an alarm clock program that triggers a daily alarm. The alarm has three modes: visual (a blinking icon), audio (a buzzer), or both audio and visual. The alarm clock is represented by a class named AlarmClock. To represent the three alarm modes, the AlarmClock class defines three constant static variables: MODE_VISUAL, MODE_AUDIO, and MODE_BOTH. Each constant is assigned a numeric value corresponding to its mode. Mode 1 is considered “visual mode,” mode 2 is considered “audio mode,” and mode 3 is considered “both visual and audio mode.” The following code shows the definitions for the mode constants:

public class AlarmClock {
  public static const MODE_VISUAL = 1;
  public static const MODE_AUDIO  = 2;
  public static const MODE_BOTH   = 3;
}

To keep track of the current mode for each AlarmClock instance, the alarm clock class defines an instance variable, mode. To set the mode of an AlarmClock object, we assign one of the mode constants’ values (1, 2, or 3) to the instance variable mode. The following code sets the default mode for new AlarmClock objects to audio-only (mode 2):

public class AlarmClock {
  public static const MODE_VISUAL = 1;
  public static const MODE_AUDIO  = 2;
  public static const MODE_BOTH   = 3;

  private var mode = AlarmClock.MODE_AUDIO;
}

When it comes time to signal an alarm, the AlarmClock object takes the appropriate action based on its current mode. The following code shows how an AlarmClock object would use the mode constants to determine which action to take:

public class AlarmClock {
  public static const MODE_VISUAL = 1;
  public static const MODE_AUDIO  = 2;
  public static const MODE_BOTH   = 3;

  private var mode = AlarmClock.MODE_AUDIO;

  private function signalAlarm () {
    if (mode == MODE_VISUAL) {
      // Display icon
    } else if (mode == MODE_AUDIO) {
      // Play sound
    } else if (mode == MODE_BOTH) {
      // Display icon and play sound
    }
  }
}

Note that in the preceding code, the mode constants are not technically necessary. Strictly speaking, we could accomplish the same thing with literal numeric values (magic values). However, the constants make the purpose of the numeric values much easier to understand. For comparison, the following code shows the AlarmClock class implemented without constants. Notice that, without reading the code comments, the meaning of the three mode values cannot easily be determined.

public class AlarmClock {
  private var mode = 2;

  private function signalAlarm () {
    if (mode == 1) {
      // Display icon
    } else if (mode == 2) {
      // Play sound
    } else if (mode == 3) {
      // Display icon and play sound
    }
  }
}

Now let’s move on to the counterpart of static variables: static methods.

Static Methods

In the preceding section we learned that static variables are used to track information that relates to an entire class. Similarly static methods define functionality that relate to an entire class, not just an instance of that class. For example, the Flash runtime API includes a class named Point that represents a Cartesian point with an x-coordinate and a y-coordinate. The Point class defines a static method, polar( ), which generates a Point object based on a given polar point (i.e., a distance and an angle). Conceptually, converting a polar point to a Cartesian point is a general service that relates to Cartesian points in general, not to a specific Point object. Therefore, it is defined as a static method.

Like instance methods, static methods are created using function definitions within class definitions, but static method definitions must also include the static attribute, as shown in the following generalized code:

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

As with instance methods, access-control modifiers can control the accessibility of static methods in a program. The access-control modifiers available for static-methods definitions are identical to those available for instance-method definitions—namely: public, internal, protected, and private. When no modifier is specified, internal (package-wide access) is used. When a modifier is specified, it is typically placed before the static attribute, as shown in the following code:

class SomeClass {
  public static function methodName (identifier1 = value1,
                                     identifier2 = value2,
                                     ...
                                     identifiern = valuen) {
  }
}

To invoke a static method, we use the following general code:

SomeClass.methodName(value1, value2,...valuen)

In the preceding code, SomeClass is the class within which the static method is defined, methodName is the name of the method, and value1, value2,...valuen is a list of zero or more method arguments. Within the class that defines the method, methodName can be used on its own (without the leading class name and dot). For example, in a class, A, that defines a static method m, the expression A.m( ) is identical to the expression m( ). Nevertheless, to distinguish static methods from instance methods, many developers (and this book) include the leading class name even when it is not strictly required.

Some classes exist solely to define static methods. Such classes group related functionality together, but objects of the class are never instantiated. For example, the built-in Mouse class exists solely to define the static methods show( ) and hide( ) (used to make the system pointer visible or invisible). Those static methods are accessed through Mouse directly (as in, Mouse.hide( )), not through an instance of the Mouse class. Objects of the mouse class are never created.

Static methods have two limitations that instance methods do not. First, a class method cannot use the this keyword. Second, a static method cannot access the instance variables and instance methods of the class in which it is defined (unlike instance methods, which can access static variables and static methods in addition to instance variables and other instance methods).

In general, static methods are used less frequently than static variables. Our virtual zoo program does not use static methods at all. To demonstrate the use of static methods, let’s return to the email validation scenario presented earlier in Chapter 2. In that scenario, we created a loop to detect whether or not an email address contains the @ character. Now let’s imagine that our application has grown large enough to warrant the creation of a utility class for working with strings. We’ll call the utility class StringUtils. The StringUtils class is not meant to be used to create objects; instead, it is merely a collection of static methods. As an example, we’ll define one static method, contains( ), which returns a Boolean value indicating whether a specified string contains a specified character. Here’s the code:

public class StringUtils {
  public function contains (string, character) {
    for (var i:int = 0; i <= string.length; i++) {
      if (string.charAt(i) == character) {
        return true;
      }
    }
    return false;
  }
}

The following code shows how our application would use the contains( ) method to check whether an email address contains the @ character:

StringUtils.contains("me@moock.org", "@");

Of course, in a real application, the email address would be supplied by the user and then contains( ) would determine whether or not to submit a form. The following code demonstrates a more realistic situation:

if (StringUtils.contains(userEmail, "@")) {
  // Code here would submit the form
} else {
  // Code here would display an "Invalid data" message to the user
}

In addition to the static methods we create ourselves, ActionScript automatically creates one static method, known as the class initializer, for every class. Let’s take a look.

The Class Initializer

When ActionScript defines a class at runtime, it automatically creates a method named the class initializer and executes that method. In this class initializer, ActionScript places all of the class’s static variable initializers and all class-level code that is not a variable definition or a method definition.

The class initializer offers an opportunity to perform one-time setup tasks when a class is defined, perhaps by invoking methods or accessing variables that are external to the current class. For example, suppose we’re creating an email reader application, and we want its visual appearance to match the operating system’s graphical style. To determine which graphical theme the mail reader should use, the application’s main class, MailReader, checks the current operating system in its class initializer and sets a corresponding static variable, theme. The theme variable dictates the graphical theme used throughout the application. The following code shows the class initializer for MailReader. To check the operating system, MailReader uses the static variable, os, defined by the built-in flash.system.Capabilities class.

package {
  import flash.system.*;

  public class MailReader {
    static var theme;
    if (Capabilities.os == "MacOS") {
      theme = "MAC";
    } else if (Capabilities.os == "Linux") {
      theme = "LINUX";
    } else {
      theme = "WINDOWS";
    }
  }
}

Code in the class initializer runs in interpreted mode, and is not compiled by the JIT compiler. Because JIT-compiled code generally executes much more quickly than interpreted code, you should consider moving processor-intensive code out of the class initializer when performance is a priority.

Class Objects

Earlier we learned that each static method and static variable is accessed through the class that defines it. For example, to access the static variable maxCalories, which is defined by the VirtualPet class, we use the following code:

VirtualPet.maxCalories

In the preceding code, the use of the class name VirtualPet is not merely a matter of syntax; VirtualPet actually refers to an object that defines the variable maxCalories. The object referenced by VirtualPet is an automatically created instance of the built-in Class class.

Every class in ActionScript is represented at runtime by an instance of the Class class. From a programmer’s perspective, Class objects are used primarily to access the static variables and static methods of a class. However, like other objects, Class objects are values that can be assigned to variables, and passed to or returned from methods and functions. For example, the following revised version of our VirtualZoo class assigns the Class object representing the VirtualPet class to a variable, vp, and then uses that variable to create a VirtualPet object:

package zoo {
  public class VirtualZoo {
    private var pet;

    public function VirtualZoo () {
      var vp = VirtualPet;
      pet = new vp("Stan");
    }
  }
}

The preceding technique is used when one .swf file wishes to access another .swf file’s classes, and when embedding external assets (such as images or fonts) in a .swf file. We’ll study both of those scenarios in Part II of this book.

We’ve now finished our study of static variables and static methods. Before we move on to the next chapter, let’s compare some of the terms we’ve learned with those used in C++ and Java.

C++ and Java Terminology Comparison

The concepts of instance variables, instance methods, static variables, and static methods are found in most object-oriented languages. For comparison, Table 4-1 lists the equivalent terms used by Java and C++.

Table 4-1. Terminology comparison

ActionScript

Java

C++

instance variable

field or instance variable

data member

instance method

method

member function

static variable

class variable

static data member

static method

class method

static member function

On to Functions

We’ve learned that an instance method defines a behavior related to a given object and a static method defines a behavior related to a given class. In the next chapter, we’ll study functions, which define standalone behaviors that are not related to any object or class.

Get Essential ActionScript 3.0 now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.