As you probably know, abstract classes are not a language feature of ActionScript 3. If you are unfamiliar with the concept of an abstract class, let me explain. Think of an abstract class as a super-powered interface. It defines a series of functions that must be implemented, and it cannot be instantiated directly. It goes a step beyond an interface because it also defines some functions that are already fully implemented.
To use an analogy, all electronic devices generally have the same connector to plug into the wall. However, not all electronics have the same purpose. A coffee maker and a DVD player do very different things. We could make an interface IElectronicDevice to make sure they all have a plugIntoWall() function, but then every device will need to re-implement the common wall plug functionality that doesn’t differ very often. By making a class AbstractElectronicDevice, we can implement the wall plug functionality once, and all subclasses of AbstractElectronicDevice will be able to use it without re-implementing the same code.
The problem, of course, is that the coffee maker and DVD player have different controls for turning power on and off. The coffee maker might have a switch, while the DVD player has a button. We can’t implement the togglePower() function in AbstractElectronicDevice because many electronics will have different controls, so we need some way to force all subclasses of AbstractElectronicDevice to implement this function themselves. Using the methods developed to enforce Singletons in ActionScript 3 as a guide, I’ve found a way to enforce the abstractness of a class at runtime upon instantiation.
There are two main parts to enforcing an abstract class. First, we must stop a developer from instantiating the abstract class directly. To do this, the constructor needs a little special sauce. Since anyone can create an instance of a public class by simply using the new keyword, we need the constructor of our abstract class to require a parameter that only subclasses will be able to pass. Second, we must ensure that a developer implements functions that the abstract class has defined, but not implemented.
Stopping Direct Instantiation of an Abstract Class
The magic bean to stop a developer from instantiating a class directly is one keyword: this. You don’t have access to an instance of a class until after you call a constructor, so only an instance of a subclass will be able to pass a reference to itself to the super class. Let’s look at some code to help make things clearer.
The instance of MyAbstractType expects to receive a reference to itself as the first parameter in the constructor. If it does not, an error will be thrown.
package com.joshtynjala.abstract
{
import flash.errors.IllegalOperationError;
public class MyAbstractType
{
public function MyAbstractType(self:MyAbstractType)
{
if(self != this)
{
//only a subclass can pass a valid reference to self
throw new IllegalOperationError("Abstract class did not receive reference to self. MyAbstractType cannot be instantiated directly.");
}
}
}
}
Only MyConcreteType and other subclasses of MyAbstractType will be able to pass a reference to the instance to their super() constructors. Notice that users of MyConcreteType don’t need to know that it extends an abstract class. The signature of the MyConcreteType’s constructor can be completely different than MyAbstractType.
package com.joshtynjala.abstract
{
public class MyConcreteType extends MyAbstractType
{
public function MyConcreteType()
{
//pass "this" to clear the abstract check
super(this);
}
}
}
Forcing Implementation of Functions in Subclasses
Next, like with an interface, we need to force subclasses of our abstract class to implement specific functions. Ideally, we want this enforcement to happen immediately when the object is created (to make bugs visible as early as possible). After we check that the user isn’t trying to instantiate the abstract class directly, we should check to be sure the unimplemented methods are overridden. We can do by checking the results from the flash.utils.describeType() function against a list of unimplemented methods in the abstract class. Again, a little code should give us a clearer picture.
In MyAbstractType, after we check for a reference to the self parameter, we build a list of unimplemented functions in an Array. Next, we use describeType() to get a list of the methods declared in the instance. If a subclass of MyAbstractType overrides a method, the declaredBy attribute in the method XML will specify the name of the subclass rather than MyAbstractType.
package com.joshtynjala.abstract
{
import flash.errors.IllegalOperationError;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
public class MyAbstractType
{
public function MyAbstractType(self:MyAbstractType)
{
if(self != this)
{
//only a subclass can pass a valid reference to self
throw new IllegalOperationError("Abstract class did not receive reference to self. MyAbstractType cannot be instantiated directly.");
}
//these functions MUST be implemented in subclasses
var unimplemented:Array = [mustBeOverridden];
//get the fully-qualified name the abstract class
var abstractTypeName:String = getQualifiedClassName(MyAbstractType);
//get a list of all the methods declared by the abstract class
//if a subclass overrides a function, declaredBy will contain the subclass name
var selfDescription:XML = describeType(this);
var methods:XMLList = selfDescription.method.(@declaredBy == abstractTypeName && unimplemented.indexOf(this[@name]) >= 0);
if(methods.length() > 0)
{
//we'll only get here if the function is still unimplemented
var concreteTypeName:String = getQualifiedClassName(this);
throw new IllegalOperationError("Function " + methods[0].@name + " from abstract class " + abstractTypeName + " has not been implemented by subclass " + concreteTypeName);
}
}
//implemented
public function alreadyImplemented():void
{
trace("Don't forget to list me in the Array of valid functions.");
}
//unimplemented
public function mustBeOverridden(param:String):void {};
}
}
Now, in MyConcreteType, we can implement the mustBeOverridden() function. If we do not, an error will be thrown. To be certain, try commenting out the implementation of mustBeOverridden() in MyConcreteClass. If you instantiate it, you will receive a runtime error.
package com.joshtynjala.abstract
{
public class MyConcreteType extends MyAbstractType
{
public function MyConcreteType()
{
//pass "this" to clear the abstract check
super(this);
}
//implemented
override public function mustBeOverridden(param:String):void
{
trace("param:", param);
}
}
}
Conclusions
As you probably noticed, the first part of implementing an abstract class is very simple. Making the abstract class receive a reference to itself enforces subclassing, and it can be done in only a few lines of code. The second part, making an abstract class work like an interface, requires a lot more code, and it may be more prone to errors. You, as a developer, must remember to add all the unimplemented methods to the Array in the constructor. Additionally, if the class gets large, I can imagine that calling describeType() often enough could lead to performance problems. In most cases, as long as you keep your classes clean, it should work well. I highly recommend the subclass enforcement, but I wouldn’t be heartbroken if you feel that checking for unimplemented methods is overkill.
Source code for this tutorial is available for download. It includes all the code above plus an extra implementation (for comparison) of the “electronic device” example I described at the beginning. It’s all under the terms of the MIT license.
I think I’m against trying to force AS3 to have language features it just doesn’t have. Especially because most of these solutions utilize runtime exceptions like this, which is not what a native language feature would do. It would just fail to compile.
I agree, to a certain extent, Keith. Arguably, if you want a class to be a singleton or an abstract class, you can easily treat that class like either without any sort of enforcement. With a little documentation if you’re working with other developers, it’s easy. In the first draft of this post, I referred to the method of creating singletons in AS3 a hack. I meant it as a simple comparison. This is also a hack. No denial there.
On the other hand, I knew people would be interested. When singletons in AS3 were first introduced, there was a lot of interesting discussion, and I wanted to start a dialog. There are many developers out there who want to push non-native features like singletons and abstract classes into their daily development because these features can be useful at times.
I agree that runtime exceptions aren’t ideal, but as long as they stick to the constructor, they’re probably the best bet. Thanks for your thoughts, Keith. I appreciate it.
‘I agree, to a certain extent, Keith. Arguably, if you want a class to be a singleton or an abstract class, you can easily treat that class like either without any sort of enforcement. With a little documentation if you’re working with other developers, it’s easy. In the first draft of this post, I referred to the method of creating singletons in AS3 a hack. I meant it as a simple comparison. This is also a hack. No denial there.’
Thats true but it also makes sense to me to enforce these kind of things, so no matter how much documentation or discuss has been had, mistakes cannot be made. I’d rather my app throw and runtime error, than it do nothing at all and therefore fall over silently.
[...] zeuslab gibt es aber nun einen Artikel der eine Möglichkeit beschreibt dieses zu Simulieren. [...]
That was very educative and interesting.
Thanks a lot Josh.
Before even reading any of the other comments, my thoughts were the same as Keith’s. Although the singleton hack is one that I could see myself using on the right occasion, this one seems a bit much. Verifying the functions seems like it could eventually become a hard cpu hit; but not verifying the functions means that you don’t really have the power of an abstract class.
It seems like the real solution is to cry loud enough till Adobe/Ecma gives us Singletons, abstract classes, etc…
I do, however, tip my hat to you for your ability to think outside the box and come up with such creative hack
Thanks for your thoughts, Nate. I know I would definitely like to see abstract classes and less hacky support for singletons in ECMAScript.
For the record, I’ll personally probably only use the no-instantiation part of my code most of the time when I need abstract classes. With only a couple lines of code to set up, it’s better than no checks at all, which is what I’ve seen from most classes that are considered abstract by a developer (such as those I’ve seen so far in ActionScript 3.0 Design Patterns, an excellent book).
I wrote a small utility class that throws a runtime error using this same type introspection method back in May.
I do agree with Keith that it would be nice if one could throw a compile time error rather than a runtime exception as there is probably a big performance hit when checking an XML file using E4X during class initialization.
Thanks for pointing that out Chandima. Great minds think alike and all that. I also followed some links and noticed that both Tink and Mims Wright have explored this area too. I guess I never noticed. Thanks for sharing!
It seems to me that the E4X usage may not be a major performance hit. It’s possible, though. I think I’ll try to do a follow up with some measurements to clear up or confirm this worry that we’ve all expressed.
Hi I have a another version of abstract class, my version do not check all the abstract function whether or not be overridden at initial time, but just verify it when you invoke it.
p.s. Just read the codes, don’t be disturbed by Chinese just ignore it.:)
Thanks for sharing, Jeff. During my planning, I considered implementing it like that, but I thought it would be better to check for overrides as early as possible (the constructor). As I and others have stated, a compile time error would be best, since it informs the user of the class immediately that something went wrong. Having all the runtime checks in the constructor is almost like the compiler check (at least when compared to the unpredictable timing of checking in the functions themselves).
Getting some performance data would indeed be great Jeff. I really don’t see anyone implementing runtime checks for abstract classes (maybe worried about the potential performance hit). Looks like the preferred way to deal with them is to comment the code … // please extend / subclass don’t instantiate etc. However, everything would be much cleaner if they were available. I’ve recently started to use the excellent PureMVC framework and it has several concrete classes that should be treated as abstract (and a few singletons) that are heavily commented.
Much like many singleton workarounds I have seen, it should be noted that MyAbstractClass can still be instantiated by putting ‘null’ in the constructor. A little extra code could make it throw an error on null as well.
Daryl, did you read the code? MyAbstractType throws an IllegalOperationError if the parameter is equal to anything but
this, includingnull.I guess I hadn’t read it that well and followed the code. Good call.
The only pain (besides the run-time errors) is if I want my MXML class to extend the abstract class I’m not able to use the constructor.
Good call, Bjorn. I hadn’t considered the possibility of using MXML.
I’m looking at it now.
I think I found something although my regexp is not up to scratch..
first couple lines of the AbstractClass.describeType(this) xml output is this.
That should be enough to do the checking that the self constructor reference was used for ?
obviously the xml code is pasted didn’t display well ;P
line 1: type name=”com.qdc.moduleprep::PreperationModule” base=”com.qdc.moduleprep.abstract::AbstractPreparationModule” isDynamic=”false” isFinal=”false” isStatic=”false”
line 2: extendsClass type=”com.qdc.moduleprep.abstract::AbstractPreparationModule”
[...] Check it out here: Astract Classes in AS3 [...]
this is a very nice post!
i disagree that functionalities that are available in one language but not in another shouldnt be emulated! Thats a really narrow view.. of course it should stay reasonable. But a statement like that would forbid implementing AOP in java or learning from other language communities.
the only thing i miss above is a more generic solution that also would allow multiple inheritance. for that reason i added two functions. i am not 100% happy with them but may be they will inspire somebody for a smoother solution…
/** * Note: use this function to create or add methods that needed to be implemented! * example: super(self,abstractMethods("ClassName",["methodAName","methodBName"])) * * @param abs passed list of abstract methods * @param clazz name of the class defining the abstract classes * @param methods list of all methods * @return list of all abstract methods that are not implemented */ protected function abstractMethods(clazz:String,methods:Array,abs:XMLList=null):XMLList{ var t:XML = ; for each(var s:String in methods) t.appendChild(); if(abs==null) abs = XMLList(t); else abs += t; return abs; } /** * @param abstract list of methods to implement * @param implemented list of methods implemented * @return a list of methods that are not implemented */ private function _checkMethods(implemented:XMLList,abstract:XMLList):XMLList{ var found:XMLList; var match:XMLList; for each(var type:XML in abstract){ for each(var method:String in type['method'].@name){ match = implemented.(@name==method && @declaredBy==type.@name); if(match.length()>0){ if(found==null) found = match; else found += match; } } } return found; }hello,
Great article but I’m a bit confused with the theory behind this concept.
You say :
“It defines a series of functions that must be implemented, and it cannot be instantiated directly. It goes a step beyond an interface because it also defines some functions that are already fully implemented.”
But in O’reilly’s Essential Actionscript 3.0 in the Chapter 6 on inheritance, there is a page “Abstract Not Supported” that, if I understand well, state the opposite. “An abstract class in any class that defines zero, or more abstarct methods”. (…)
Subclasses of an abstract class effectively promises to provide some real code to do a job the abstract class only describes in theory.
(…)
I’ve read some other blogs that describes the uses of Abstract class the way you do.
But that book is a trustable source so Am I missing something?
Thks!
greg, the definition of an abstract class in EAS3 does not contradict my definition.
When I say “it defines a series of functions that must be implemented”, that’s the same as “subclasses of an abstract class effectively promise to provide some real code”.
What is it that you believe describes the opposite?
Hey Greg, I’ve always thought of abstract classes exactly the way Josh describes them. The O’reilly quote doesn’t contradict Josh’s definition in my opinion. An abstract class contains “zero or more” abstract methods as you mention.
You can declare your class as abstract without having any abstract methods inside it. In this case it’s just functioning as a base class, but the compiler prevents anyone from instantiating the base class on it’s own. It would most likely have some regular non-abstract methods though, otherwise it wouldn’t be serving much purpose & you might as well use an interface instead.
If you do have an abstract method in your class, though, as far as I know the whole class becomes abstract whether you declare it that way or not. In fact depending on the language, you may get a compiler error if you try to make a method abstract without also declaring the class as abstract.
I do find abstract classes helpful, check out the abstract factory pattern for one example. It would be nice to see this functionality added to the compiler.
“…most of these solutions utilize runtime exceptions like this, which is not what a native language feature would do. It would just fail to compile”
not at all. this code
var foo:DisplayObject = new DisplayObjectcompiles fine, but throws this exception:It appears that this only works if the abstract method is public. Private and protected methods don’t show up in selfDescription.method
Just put an exception in the methods that must be overloaded, and remove that reflection (describeType). Performance solved. Private/protected solved.
Eric, you are right. this will be only a little extra work…
Maybe I’ll combine the two options. I’ll see.
Excellent post. Thanks for sharing. How about adding this function declaration to the abstract class to enforce concrete class implementation :
protected function doSomething(): void { throw new IllegalOperationError("This method must be implemented in the concrete class."); }Excellent post. Thanks for sharing. How about adding this function declaration to the abstract class to enforce concrete class implementation :
protected function doSomething(): void {
throw new IllegalOperationError(“This method must be implemented in the concrete class.”);
}
First, thank you Josh for posting this.
Second,
protected function doSomething(): void { throw new IllegalOperationError(â€This method must be implemented in the concrete class.â€); }… is not the same as introspection as it does not produce an error until the function is called — the worst possible case in my mind.
I agree with the other posters when they say that Adobe needs to provide an abstract class naively in the language, however, they do not and there are some things you need abstract classes to do (being a C# programmer professionally I really miss this functionality).
Additionally, I always see a good number of people complaining about the “cost” of introspection. Honestly, I use it a lot and RARELY see a serious performance hit (in which case I disable and find another way). However, I think it is backwards to consider performance first and functionality second (when performance is not necessarily going to be an issue). In my not so humble opinion most developers focus WAY to much on performance given the power of the average PC/Server. After getting your code to work, the important factors in order should be:
1. Reliability
2. Maintainability
3. Performance (this is NOT where most applications fall short or cost money)
On the other hand, I believe the compile time error is important and does address the first 2 issues. Without that, the next best thing is to throw an exception at runtime and as soon as possible. In fact, in my own solution, I use even MORE introspection and find all the classes that extend the “MyConcreteType” at initialization and throw exceptions BEFORE the application even fully starts. This makes development, maintainance, etc pretty easy and adds only a mere second or two to my init process (unlike say drawing the controls which takes much longer =P)
Dont fear introspection! I have seen a lot of slow buggy flash/flex applications and more often than not they are slow because of “clever” UI design decisions.
As with most introspection I found describeType to be pretty expensive, so I recommend caching the results somewhere so that you don’t need to call describeType twice for the same type.
[...] Self Reference Approach @ JoshTalksFlash [...]
Great Post! i read that in order to use decorator pattern on Sprite/Movie Clip based objects you use a abstract-y class not a inteface def – i will try out your suggestion here
It might be a while ago since you wrote this but still very interesting/inspiring to see someone so dedicated to create an abstract hack. Cool and will try to implement it anytime soon!
Greets