8.3 Interface Syntax and Use

 <  Day Day Up  >  

Recall that an interface is a construct used to define a new datatype without implementing any of the methods supported by the datatype.

To create an interface in ActionScript 2.0, we use the interface keyword, using the following syntax:

  interface   SomeName  {   function  method1  (  param1  :  datatype  ,...  paramn  :  datatype  ):  returnType  ;   function  method2  (  param1  :  datatype  ,...  paramn  :  datatype  ):  returnType  ;   ...   function  methodn  (  param1  :  datatype  ,...  paramn  :  datatype  ):  returnType  ; } 

where SomeName is the name of the interface, method1 , ... methodn are the methods defined by the interface, param1:datatype , ... paramn:datatype are the parameters of the methods, and returnType is the datatype of each method's return value.

In interfaces, method declarations do not (and must not) include curly braces. The following method declaration causes a compile-time error in an interface because it includes curly braces:

 function   method1   (   param   :   datatype   ):   returnType   { } 

The errors generated are:

 Function bodies are not permitted in interfaces. This statement is not permitted in an interface definition. 


All methods declared in an interface must be public ; hence, by convention, the attribute public (which is the default) is omitted from method definitions in an interface. Property definitions are not allowed in ActionScript 2.0 interfaces; neither can interface definitions be nested (this contrasts with Java's interfaces, in which constant definitions are allowed and interfaces can be nested). As with ActionScript 2.0 classes, each interface must be defined in its own .as file, whose name must exactly match the name of the interface (case sensitivity matters).

Let's briefly review the three types of .as files we've covered so far:


Class files

Contain the method and property definitions for each class. They start with the class keyword. (See Chapter 4.)


Intrinsic files

Contain method signatures strictly for the purpose of satisfying the compiler's type checking. They start with the intrinsic keyword. (See Chapter 4.)


Interface files

Contain a list of methods to be implemented but not the implementations themselves . They start with the interface keyword.

A class that wishes to adopt an interface's datatype must agree to implement that interface's methods. To form such an agreement, the class uses the implements keyword, which has the following syntax:

 class   SomeName   implements   SomeInterface   { } 

where SomeName is the name of the class that promises to implement the needed methods, and SomeInterface is the name of the interface (as defined using the interface keyword in an external .as file). The SomeName class is said to "implement the SomeInterface interface." Note that implements must always come after any extends clause that might also be present. Furthermore, if you specify a class instead of an interface after the implements keyword, the compiler generates this error:

 A class may not implement a class, only interfaces. 

The class SomeName must implement all methods defined by SomeInterface , otherwise a compile-time error such as the following occurs:

 The class must implement method 'methodname' from interface 'SomeInterface'. 

The implementing class's method definitions must match the interface's method definitions exactly, including number of parameters, parameter types, and return type. If any of those aspects differs between the interface and the implementing class, the compiler generates the following error:

 The implementation of the interface method doesn't match its definition. 

In theory, when a class implements a method from an interface, it should be able to define the method as either a class method (i.e., static ) or an instance method. Unfortunately, a bug in ActionScript 2.0 prevents a class from using the static attribute when implementing a method defined by an interface. Due to the bug, a method defined by an interface cannot be implemented as a class method. If the method in the implementing class must be a class method, then it should not be listed in the interface at all.


A class can legally implement more than one interface by separating interface names with commas, as follows :

 class   SomeName   implements   SomeInterface   ,   SomeOtherInterface   { } 

in which case, the class SomeName belongs to all three of the following datatypes: SomeName , SomeInterface , and SomeOtherInterface . If a class implements two interfaces that define a method by the same name, but with different signatures, the following error occurs:

 Multiple implemented interfaces contain same method with different types. 

If, on the other hand, a class implements two interfaces that define a method by the same name and with the exact same signature, no error occurs. The real question is whether the class can provide the services required by both interfaces within a single method definition. In most cases, the answer is no.

Once an interface has been implemented by one or more classes, adding new methods to it will cause compile-time errors in those implementing classes (because the classes won't define the new methods)! Hence, you should think carefully about the methods you want in an interface during the design phase and be sure you're confident in your application's design before you commit it to code.


If a class declares that it implements an interface, but that interface cannot be found by the compiler, the following error occurs (notice that the compiler misleadingly refers to the interface as a class!):

 The class 'InterfaceName' could not be loaded. 

8.3.1 Interface Naming Conventions

Like classes, interfaces should be named with an initial capital letter so they're easy to identify as datatypes. Interfaces used for event handling typically take the form SomeClass Listener , where SomeClass is the name of the class broadcasting the events. For example, in Chapter 19, we'll create a class named Randomizer that broadcasts events. Classes wishing to process Randomizer events must implement RandomizerListener .

Most other interfaces are named after the additional ability they describe. For example, suppose an application contains a series of classes that represent visual objects. Some of the objects can be repositioned; others cannot. In our design, objects that can be repositioned must implement an interface named Moveable . Here is a theoretical ProductIcon class that implements Moveable :

 class ProductIcon implements Moveable {   public function getPosition ( ):Position {   }   public function setPosition (pos:Position):Void {   } } 

The interface name, Moveable , indicates the specific capability that the interface adds to a class. An object might be a piece of clipart or a block of text, but if it implements Moveable, we know it can be repositioned. Other similar names might be Storable , Killable , or Serializable . Some developers also preface interface names with an I, as in IMoveable , IKillable , and ISerializable .

8.3.2 Interface Inheritance

As with classes, an interface can use the extends keyword to inherit from another interface. For example, the following code shows an interface, IntA , that extends another interface, IntB . In this setup, interface IntB is known as the subinterface, and interface IntA is known as the superinterface .

 interface IntA {   function methodA ( ):Void; } interface IntB extends A {   function methodB ( ):Void; } 

Classes that implement interface IntB must provide definitions for both methodA( ) and methodB( ) . Interface inheritance lets us define a type hierarchy much as we would with class inheritance, but without accompanying method implementations.

Unlike Java, ActionScript 2.0 interfaces do not support multiple interface inheritance; that is, an interface cannot extend more than one other interface.

8.3.3 Marker Interfaces

Interfaces need not contain any methods at all to be useful. Occasionally, empty interfaces, called marker interfaces , are used to "mark" ( designate ) a class as having some feature. Requirements for the marked classes (i.e., classes implementing a marker interface) are provided by the documentation for the marker interface.

For example, our earlier OrderProcessor class used the OrderListener interface to ensure that the appropriate event methods are defined on any object wishing to receive event notifications.

Now consider a similar but more complex situation ”a web-based email application that contains three classes: MessageComposer , MessageSender , and MessageReceiver . Each class wants to broadcast its own events. The event-handling methods required of every event-receiving object are defined in the following corresponding interfaces: MessageComposerListener , MessageSenderListener , and MessageReceiverListener . In the email application, because more than one class broadcasts events, it's sensible to define a general class, EventListenerList , to manage objects that receive events (known as event-consumer objects ). Each of the MessageComposer , MessageSender , and MessageReceiver classes uses an EventListenerList instance to manage event-consumer objects. For example, when a MessageComposer instance wants to add a new event-consumer object to its EventListenerList , it passes that object to EventListenerList.addListener( ) , as follows:

 listenerList.addListener(consumerObject); 

Can you spot the problem? The event-consumer objects for each of the event-producing classes belong to different datatypes! A consumer object for the MessageComposer class belongs to the MessageComposerListener datatype, while a consumer object for the MessageSender class belongs to the MessageSenderListener datatype. The addListener( ) method can accept only one type of consumer object as an argument, but it needs to be used with multiple datatypes.

Solution? Make all event-consumer objects homogeneous by creating a marker interface, EventListener . The three listener interfaces in the mail application extend EventListener . The addListener( ) method then accepts consumer objects of type EventListener . Hence, different types of event-consumer objects ( specifically , subtypes of EventListener ) can be passed to the method. The method definition for addListener( ) follows (note the use of the marker interface, EventListener , shown in bold):

 public function addListener (  l:EventListener  ):Boolean {   // Search for the specified event consumer.   //   listeners   is an array of event-consumer objects.   var len:Number = listeners.length;   for (var i:Number = len; --i >= 0; ) {     if (listeners[i] == l) {       // The new event consumer is already in the list, so quit.       return false;     }   }   // The new listener is not already in the list, so add it.   listeners.push(l);   return true; } 

Note that we could have solved the problem alternatively by allowing any object to be used as an event-consumer object, as follows:

 public function addListener (  l:Object  ):Boolean { 

That works in the technical sense but does nothing to suggest to the developer that event-consumer objects must implement one of the application's event-listener interfaces. By forcing event-consumer objects to implement the marker interface, EventListener , the application guides the developer to the documentation for that interface. The documentation should describe the application's general event architecture, helping the developer understand how to properly handle events. This system is a native part of Java. We'll return to it in Chapter 19.

 <  Day Day Up  >  


Essential ActionScript 2.0
Essential ActionScript 2.0
ISBN: 0596006527
EAN: 2147483647
Year: 2004
Pages: 177
Authors: Colin Moock

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net