Reputation: 218
I have a potentially impossible desire. I have a class, from which I want to build several subclasses. Each subclass will implement the same interface, but I kind of want the BASE class to call methods of that interface.
Here's the compiler-error-y version of what I want:
public class Class1
{
public function Class1()
{
initialize();
}
}
public interface Int
{
function initialize():void;
}
public class Class2 extends Class1 implements Int
{
public function Class2()
{
super();
}
public function initialize():void
{
// Code unique to the class goes here.
}
}
Obviously, this code breaks, even though I have no intention to create an instance of Class1. It also looks dumb in this simplified version, but it kind of shows the reason why I want this: I want each class to automatically call initialize()
in its constructor, thus it makes sense to keep it in the superclass.
Now, I could do something like this:
public class Class1
{
public function Class1() implements Int
{
initialize();
}
public function initialize():void {}
}
public interface Int
{
function initialize():void;
}
public class Class2 extends Class1 implements Int
{
public function Class2()
{
super();
}
public override function initialize():void
{
// Code unique to the class goes here.
}
}
This is kind of where I'm at right now, but placing the method in the superclass and overriding it in the subclasses defeats the purpose of having the interface; the initialize()
method requirement is met simply by extending the original class, and there is no way I know of that I can FORCE each subclass to override that method. I can, of course, omit the call in the base class and insert it into each of the subclasses, but I can't enforce the requirement to have THAT either.
So my question is, is there a version of #1 (calling interface methods in a base class that doesn't implement the interface) that won't break, or a version of #2 (overriding the method in question) that will REQUIRE initialize() to be overridden by the subclass?
Upvotes: 2
Views: 1412
Reputation: 584
Its an old topic, but I will post my minds. It makes no sense, to call super() and then make a callback to init(). Unnecessary calls... its like ping-pong.
It's better do define init() as private, because its unique SubClass-Code and should not be accessible from outside the class. You don't need an Interface to make sure a private init()-method exist. If you call it in the constructor and it doesn't exist it won't compile - same behavior like with Interfaces. If another individual SubClass don't need an init(), just don't call/define it or leave it empty. Simple, huh? ^^
Interfaces should only guarantee, that PUBLIC functions are available for other coders. Nobody needs to know about private functions. It's your own responsibility to write clean and uniform code inside your Classes. Private methods are always optionally in each individual Class.
Lets say you want initialize your individual SubClasses with Interfaces (of public methods) - for example a setSize(x,y) method.
public interface IBase
{
function setSize(x:int, y:int):void
}
[abstract] public class Base
{
private var _x:int;
private var _y:int;
public function Base()
{
if(!(this is IBase))
{
throw new ArgumentError("Class " + flash.utils.getQualifiedClassName(this) + " can not be instantiated.");
}
}
protected function init():void
{
// global init functions...
}
protected function setSize(x:int, y:int):void
{
this._x = x;
this._y = y;
}
}
In the constructor you can check if "this" (the SubClass) implements the IBase interface. So nobody can instance the BaseClass directly. Like an abstract class. Btw: The [abstract] MetaTag does nothing. Its just for better understanding the class. ;)
All other functions are protected. They are only accessible from a SubClass - they are not accessible from outside. The setSize() function is not recognized by the Interface, because its protected, not public!
public class Sub extends Base implements IBase
{
public function Sub()
{
// You don't need to define an empty super(). The compiler will do it for you automatically.
// It's only necassary, if you want to pass parameters to the BaseClass.
// The Base-Constructor fires always before Sub() and init()
init();
}
// Its private and save. Only this class can call this individual init().
private function init():void
{
// init something
super.init(); // optionally call the protected [Base].init()
}
// Its public and necessary, because its defined in the Interface
public function setSize(x:int, y:int):void
{
// Do something before
super.setSize(x, y);
// Do something after
// or do something special in the SubClass
}
}
Now you have to set the public functions in your SubClasses which you have defined in your Interface. No override! We just invoke the Base functions. You can do anything what you want inside this public function and can call the protected function in the BaseClass via super.setSize(x,y).
Nobody can access the private init() from outside - thats good to prevent double inits. But everybody can call setSize() from outside and every SubClass had to implement this due the Interface - thats what you want. The same-named protected functions in the BaseClass are irrelevant - they can have other names, too - its just for unification.
If you don't define a individual private init() function, it automatically calls the Base.init() due the protected behavior.
And do you know what a constructor is for? Yes, initialization! Why do you not just write your individual init-code in your SubClass-Constructor and get rid of the additionally init-Function? ;)
Upvotes: 0
Reputation: 39466
If I interpreted your question correctly, you want to have an Interface implemented on subclasses of Class1
, but not Class1
directly. You also want to be able to call the methods within the Interface from Class1
, so that they are automatically called when you subclass it.
I'll point out now that this potentially bad design, but regardless here's what you can do:
The Interface:
A basic interface for testing:
public interface Interface
{
function init():void;
}
Your base class:
This is the class that will call init()
if any subclasses implement Interface
:
public class Base
{
public function Base()
{
// Use 'is' to check if this implements Interface.
if(this is Interface)
{
// Use 'as' to refer to .init() within Interface.
(this as Interface).init();
}
else
{
// Interface was not implemented.
// Throw an error or whatever you find necessary.
//
}
}
}
The trick here is that we use is
to determine whether the subclass has implemented Interface
or not. If it has, we can access methods defined by it via as
and go ahead and call them.
The subclass:
This is your straightforward subclass for testing:
public class Sub extends Base implements Interface
{
public function init():void
{
trace('Success!');
}
}
If you go ahead and test that by constructing an instance of Sub
, you'll notice Success!
in the output panel as expected.
Upvotes: 3
Reputation: 657
I feel your pain, an abstract class would be very useful here. Unfortunately those don't exist in AS3. The best I've been able to do in your situation is only a slight alteration from your suggestion.
public class Class1
{
public function Class1() implements Int
{
initialize();
}
public function initialize():void {
throw new Error("Subclass of Class1 must override initialize.");
}
}
Obviously this will give you a run-time error, not a compile-time error. I still only use this strategy sparingly, since declaring methods and throwing errors isn't clean and is a concession. Sometimes however, the advantages are worth it.
Upvotes: 0
Reputation: 571
Have the base class extend the interface. And then the sub class override base methods. This will allow you to call methods from your base.
Upvotes: 0