Runtime Checks for Abstract Classes and Methods in ActionScript 3.0

In AS2, declaring a constructor as private in a class prevented it from being instantiated. Unlike AS2, constructor methods cannot be declared as private in ActionScript 3.0. This killed the simple runtime check for abstract class instantation. Sho Kuwamoto, Senior Director of Engineering at Adobe describes the thought process behind the omission of private constructors in AS3.

A more creative approach to implementing abstract classes is necessary in AS3. The issue of preventing instantiation of abstract classes in AS3 has been looked into very throughly by several developers such as Tink and Mims Wright. The solution is quite simple, but how do we make sure that abstract methods have been implemented in subclasses as well? This post describes one solution (Mims has another) to this issue using the concept of type introspection in ActionScript 3.0.

Type Introspection
Type introspection enables you to look into elements of a class at runtime. The best way to do this is to use the introspection API in AS3 – specifically the flash.utils.describeType class. The describeType() method produces an XML object that describes the object passed to it. For example, let’s consider the following class.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
package {
    class SimpleClass {
        public function methodOne():void {
        }
        public function methodTwo():void {
        }
        private function methodThree():void {
        }
    }
}

If we describe an instance of this class using describeType()

?View Code ACTIONSCRIPT
1
2
3
import flash.utils.describeType;
var simpleInstance = new SimpleClass();
trace(describeType(simpleInstance));

We will get the following XML in the output panel.

1
2
3
4
5
<type name="::SimpleClass" base="Object" isDynamic="false" isFinal="false" isStatic="false">
  <extendsClass type="Object"/>
  <method name="methodTwo" declaredBy="::SimpleClass" returnType="void"/>
  <method name="methodOne" declaredBy="::SimpleClass" returnType="void"/>
</type>

The type tag attribute base indicates that the base class for this instance of SimpleClass is Object. Note that only public methods are described using introspection. The private methodThree() method is invisible to the introspection API. A method tag describes all publicly declared methods. The declaredBy attribute specifies the class that defined the method.

Introspecting a subclass

Let’s create another class that extends SimpleClass. ExtendedClass extends SimpleClass, overrides methodOne() and declares an additional method called methodFour().

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
package {
    class ExtendedClass extends SimpleClass {
        override public function methodOne():void {
        }
        public function methodFour():void {
        }
    }
}

Introspecting an instance of the ExtendedClass provides the following XML object.

1
2
3
4
5
6
7
<type name="::ExtendedClass" base="::SimpleClass" isDynamic="false" isFinal="false" isStatic="false">
  <extendsClass type="::SimpleClass"/>
  <extendsClass type="Object"/>
  <method name="methodFour" declaredBy="::ExtendedClass" returnType="void"/>
  <method name="methodOne" declaredBy="::ExtendedClass" returnType="void"/>
  <method name="methodTwo" declaredBy="::SimpleClass" returnType="void"/>
</type>

Several extendsClass tags show the inheritance hierarchy. The declaredBy attribute now shows that the methodOne() method is now declared in ExtendedClass as it was overridden.

We can leverage the introspection API to develop a runtime checker class to check for abstract class instantiation and abstract method implementation in subclasses.

Runtime checker for abstract classes and abstract method implementations

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.as3dp.utils {
 
    import flash.utils.describeType;
    import flash.utils.getQualifiedClassName;
    import flash.errors.IllegalOperationError;
 
    public class RuntimeCheck {
 
        /** 
        *   Runtime checks for abstract classes and methods
        *   Checks if callee is a subclass and has defined indicated methods.
        *   Should normally be called from the constructor of the abstract class.
        *
        *   Note: Abstract method checks only work for public functions
        *   
        *   @param      instance                calling object (the 'this' keyword)
        *   @param      abstractClass           the class name of the abstract class 
        *   @param      abstractMethodList      array of strings indicating the method names
        *                                       of abstract methods
        *   @exception  IllegalOperationError   if abstract class has not been subclassed or
        *                                       indicated methods have not been defined in subclass
        *   @author     Chandima Cumaranatunge
        *   @version    0.1
        *
        *   Usage:
        *    public class AbstractClass {
        *       public function AbstractClass() {
        *           RuntimeCheck.abstractClass(this, AbstractClass, ['abstractMethodOne', 'abstractMethodTwo']);
        *       }
        *       public function abstractMethodOne():void {}
        *       public function abstractMethodTwo():void {}
        *   }
        */
        public static function abstractClass(instance:Object, abstractClass:Class, abstractMethodList:Array = null):void {
            var instanceClassName = getQualifiedClassName(instance);
            if (instance.constructor === abstractClass) {
                throw new IllegalOperationError("Abstract class '" + instanceClassName + "' must be subclassed and not instantiated.");
            } else {
                var description:XML = describeType(instance);
                //trace(description);
                for each (var fnName:String in abstractMethodList) {
                    var overridenFlag:Boolean = false;
                    for (var pname:String in description.method) {
                        if(description.method.@name[pname].toString() == fnName) {
                            overridenFlag = (description.method.@declaredBy[pname].toString() === instanceClassName);
                        }
                    }
                    if (!overridenFlag) {
                        throw new IllegalOperationError("Abstract method '" + fnName + "' must be overridden and implemented in subclass.");
                    }
                }
            }
        }
    }
}

Note that this method utilizes E4X to parse the XML objects and can be slow in cases where speed is critical. To use the checker, simply import the package com.as3dp.utils.RuntimeCheck in the abstract class. Then, in the constructor of the abstract class, call the static method abstractClass. The first parameter is the keyword this. The second parameter is the abstract class name (passed as class). The third, optional parameter is an array of abstract method names passed as string literals. If any abstract class requirements are violated, an IllegalOperationError is thrown.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package {
 
	import com.as3dp.utils.RuntimeCheck;
 
	public class AbstractClass {
 
		// Constructor
		public function AbstractClass() {
			RuntimeCheck.abstractClass(this, AbstractClass, ['abstractMethodOne', 'abstractMethodTwo']);
		}
 
		// Abstract Method
		public function abstractMethodOne():void {
		}
 
		// Abstract Method
		public function abstractMethodTwo():String {
			return null;
		}
 
		// Implemented Method
		public function implementedMethod():void {
			/* impelementation */
		}
	}
}

The classes that extend (subclass) AbstractClass need to implement the abstractMethodOne and abstractMethodTwo methods as shown below.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package {
 
	class SubClass extends AbstractClass {
 
		// implemented abstract method
		override public function abstractMethodOne():void {
		}
 
		// implemented abstract method
		override public function abstractMethodTwo():String {
			return "";
		}
 
		public function subclassMethod():void {
		}
	}
}

Download the runtimecheck.zip AS3 Flash project to see how this works.

  • Share/Bookmark

Related posts:

  1. Abstract is as Abstract Does: A Forrest Gump Approach to Abstract Classes in ActionScript 3.0
  2. ActionScript 3.0 Abstract Factory Design Pattern: Multiple Products and Factories
  3. ActionScript Proxy Design Pattern : The Virtual Proxy (A Minimal Abstract Example)

6 Responses to “Runtime Checks for Abstract Classes and Methods in ActionScript 3.0”


  • Hey Chandima,

    I actually did figure out a way to enforce methods as well as classes with my implementation. The AbstractEnforcer class contains two methods,
    AbstractEnforcer.enforceConstructor() for making a constructor abstract
    and
    AbstractEnforcer.enforceMethod() for making a method abstract.
    You can use both independently of each other – in other words, you can have an abstract method without having an abstract class.
    Both work at runtime. What would be really great is to get one that works at compile time but I haven’t figured that out yet.
    Your solution is interesting too. There’s probably more to learn from the introspection that I haven’t found yet.

    Mims

  • Folks, this works all well and good, but three comments:

    This could should always be wrapped in something like “if (Debug)” to slap any of your developer peers who may not understand the abstract pattern; you wouldn’t want to enable this code in production because:
    1) it’s inefficient;
    2) if you catch this in production, it’s too late to do anything about what should have been a compile-time exception anyway.

    Chandima, the type introspection you do in the above code only works if you scope your virtual functions as “public”. At times this is somewhat inconvenient, since you may want to protect your functions from the above mentioned developer peers (you know who they are) who try to call your functions out of context.

    Mims, although it is possible that I haven’t seen your latest code, it seems to me that you are only enforcing 50% of what an abstract class requires. In addition to the prohibition of the abstract class being instantiated (which you do correctly), a subclass of an abstract class must implement ALL of the virtual functions of the abstract class. With your code, you only throw an exception if the virtual method is called directly (because that particular method has no override). With this implementation, the oversight of overriding a single virtual function among many may not pop up while you’re developing (if you don’t test every possible use case). I like Chandima’s solution which front-loads all the checks using type introspection.

    In summary, I’ve found implementing this pattern in AS3 to be a real pain in the butt, not to mention an ugly hack at best. I’m definitely putting my vote in for the abstract keyword in AS4.

  • Hi Kevin,

    Thanks for the insightful comments – I agree 100% with everything you said. This was more like a “Look! abstract classes in AS3″ exercise than a robust solution. I myself, just name abstract classes in AS starting with the word “Abstract”, heavily comment stuff, and rarely throw any runtime exceptions.

    Like you, I’m hoping that abstract classes will be baked into future versions of AS. However, I don’t see any movement in that direction in the ECMA proposals :/

  • If I can suggest some improvements:
    - you can use functions names to recognise abstract ones. For instance
    “abstract_FunctionName”.
    - you can use compiler constants to make “debug”, “test” and “release” versions of your code.
    - there is no word of explanation on why have you dismissed interfaces. I know those aren’t enough but while reading this I was wondering about your reasons.

  • Chandima Cumaranatunge

    Hi Arek, Thanks for your comments and suggestions. As described in my previous comment to Kevin Freund, this was not intended to be a robust solution to implementing abstract classes in AS3. All your suggestions are sound – in fact, I don’t use use this method myself, but simply name classes AbstractClassName as you suggest. Interfaces only solve one aspect of the abstract class issue. They make sure that you implement the method, but don’t allow for a default implementation. Of course, you can solve the abstract class issue quite nicely by Declaring a parent class that the abstract class needs to extend and a pure interface that it needs to implement. I didn’t dismiss pure interfaces out-of-hand, but the intention here was to try and implement abstract classes as they are found in other languages that support them. I do hope that abstract classes will be implemented in a future version of AS3 so that we don’t have to implement these convoluted solutions.

  • I think, interfaces take care most of the things therefore abstract classes are not necessary, but would like to have the “private” constructors in as3.

Leave a Reply