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:
classSomeClass
{ static varidentifier
=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:
classSomeClass
{ private static varidentifier
=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.
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 constIDENTIFIER
=value
To create a constant instance variable, we use the following generalized code directly within a class body:
constIDENTIFIER
=value
To create a constant local variable, we use the following generalized code within a method or function:
constIDENTIFIER
=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:
classSomeClass
{ static functionmethodName
(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:
classSomeClass
{ public static functionmethodName
(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++.
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.