I have an Entity class with a destroy() function.
I also have an Enemy class that extends Entity, and I want to add some lines to the destroy() function.
Is there a way to extend functions in ActionScript 3, or is copy and paste the way to go? Thanks.
You need to mark the method with the override keyword, and from there use the same namespace (public, protected, etc) and name that make up the method you want to override in the class you're extending.
The method must also have the same return type and accept the same arguments
Sample override:
override public function destroy():void
{
// add more code
super.destroy();
}
If you exclude the line which reads super.destroy(), the function within the base class will not be run, and only your new code will be used instead.
Related
I'd like to use ES6 public class fields:
class Superclass {
constructor() {
// would like to write modular code that applies to all
// subclasses here, or similarly somewhere in Superclass
this.example++; // does NOT WORK (not intialized)
//e.g. doStuffWith(this.fieldTemplates)
}
}
class Subclass extends Superclass {
example = 0
static fieldTemplates = [
Foo,
function() {this.example++},
etc
]
}
Problem:
ES6 public fields are NOT initialized before the constructors, only before the current constructor. For example, when calling super(), any child field will not yet have been defined, like this.example will not yet exist. Static fields will have already been defined. So for example if one were to execute the code function(){this.example++} with .bind as appropriate, called from the superclass constructor, it would fail.
Workaround:
One workaround would be to put all initialization logic after all ES6 public classes have been properly initialized. For example:
class Subclass extends Superclass {
example = 0
lateConstructor = (function(){
this.example++; // works fine
}).bind(this)()
}
What's the solution?
However, this would involve rewriting every single class. I would like something like this by just defining it in the Superclass.constructor, something magic like Object.defineProperty(this, 'lateConstructor', {some magic}) (Object.defineProperty is allegedly internally how es6 static fields are defined, but I see no such explanation how to achieve this programatically in say the mozilla docs; after using Object.getOwnPropertyDescriptor to inspect my above immediately-.binded-and-evaluated cludge I'm inclined to believe there is no way to define a property descriptor as a thunk; the definition is probably executed after returning from super(), that is probably immediately evaluated and assigned to the class like let exampleValue = eval(...); Object.defineProperty(..{value:exampleValue})). Alternatively I could do something horrible like do setTimeout(this.lateConstructor,0) in the Superclass.constructor but that would break many things and not compose well.
I could perhaps try to just use a hierarchy of Objects everywhere instead, but is there some way to implement some global logic for all subclasses in the parent class? Besides making everything lazy with getters? Thanks for any insight.
References:
Run additional action after constructor -- (problems: this requires wrapping all subclasses)
Can I create a thunk to run after the constructor?
No, that is not possible.
How to run code after class fields are initialized, in a sane way?
Put the code in the constructor of the class that defines those fields.
Is there some way to implement some global logic for all subclasses in the parent class?
Yes: define a method. The subclass can call it from its constructor.
Just thought of a workaround (that is hierarchically composable). To answer my own question, in a somewhat unfulfilling way (people should feel free to post better solutions):
// The following illustrates a way to ensure all public class fields have been defined and initialized
// prior to running 'constructor' code. This is achieved by never calling new directly, but instead just
// running Someclass.make(...). All constructor code is instead written in an init(...) function.
class Superclass {
init(opts) { // 'constructor'
this.toRun(); // custom constructor logic example
}
static make() { // the magic that makes everything work
var R = new this();
R.init(...arguments);
return R;
}
}
class Subclass extends Superclass {
subclassValue = 0 // custom public class field example
init(toAdd, opts) { // 'constructor'
// custom constructor logic example
this.subclassValue += toAdd; // may use THIS before super.init
super.init(opts);
// may do stuff afterwards
}
toRun() { // custom public class method example
console.log('.subclassValue = ', this.subclassValue);
}
}
Demo:
> var obj = Subclass.make(1, {});
.subclassValue = 1
> console.log(obj);
Subclass {
subclassValue: 1
__proto__: Superclass
}
I'm using PhpStorm and something that lately is bothering me a lot is this scenario. Suppose I have this setup:
// File1.php
abstract class AbstractBase {
public function __construct() {
}
}
// File2.php
class MyClass extends AbstractBase {
}
// File3.php
$var = new MyClass();
Now, if I'm reading the line if File3.php and want to go to MyClass definition if File2.php, the easiest way is to hold the Ctrl button and then click the MyClass name. This works very nicely for functions and member variables, and in PHPDoc comments, but in this case PhpStorm chooses to go to the constructor rather than the class definition. And since MyClass doesn't have a constructor of its own, it goes to the AbstractBase constructor in a completely different file.
I understand that in this case it's ambiguous what I want to do. I also know that I can right-click MyClass and then select Go To and then Type Declaration. But is there a way to configure Ctrl-Click that it would go to the class definition rather the constructor?
I know that I can create a class and then all my variables will be protected. Then I can insert functions inside this class. But to use these functions I would have to create an object of my class. I don't want to. I want only to use the functions, and they must be variable protected, and importable.
If you don't want to instantiate ("create an object") a class to use it's functions you can make your functions static.
public class MyClass {
public static function myFunction(){
trace("yada!");
}
}
// You call it this way
MyClass.myFunction();
What do you mean with "variable protected" and "importable"?
When I extend a class I want to override methods and change their accessibility like a protected method should be public in a certain class. When I compile it says: "Incompatible override". I can't reduce nor can I increase the visibility.
Reducing wouldn't make sense and I don't need it but I was able to increase method visibility in Java. Why not in ActionScript 3 ?
public class OldClass
{
protected function doStuff() : void
{}
}
public class NewClass extends OldClass
{
override public function doStuff() : void
{}
}
Am I doing something wrong ?
No, you're not doing anything wrong. That's just how the language works. From the documentation (emphasis added):
Static methods are not inherited and cannot be overridden. Instance methods, however, are inherited by subclasses and can be overridden as long as the following two criteria are met:
...
The override method must have the same level of access control as the base class method. Methods marked as internal have the same level of access control as methods that have no access control specifier.
The override method must have the same number of parameters as the base class method.
The override method parameters must have the same data type annotations as the parameters in the base class method.
The override method must have the same return type as the base class method.
I need a little help understanding how classes work in Actionscript 3. I understand you start with "package" and why and then go to import any necessary libraries, as well as then naming the class and stating if it's public/private and extends anything.
After that is what I don't understand. It seems you write "(public) function class name()
I don't understand why you do this and what goes in the curly brackets.
I've probably missed a bit of earlier reading because I've done a little reading but I can't seem to get it.
Could someone try explain it to me? Thanks.
ActionScript 3 Classes
The package statement.
Okay, so firstly like you mentioned, a class must be wrapped by a package1. This gives us the first block, where you need to define the class.
package
{
// Your class here.
}
The package statement reflects the location of the class relative to the .fla2. For example, if you have a folder "classes" within the same directory as the project .fla, then classes within that folder will need a package statement that reflects that:
package classes
{
// Your class here.
}
Defining the class.
Within a package statement, you may insert one class. Do not confuse this with the package itself, which can contain many classes - each class just needs to have its own file with the same package statement.
A class definition is made up of up to 5 parts:
The namespace. A class can be internal or public. An internal class can only be seen by classes within the same package, whereas public classes can be seen from anywhere in the project.
The class name.
A base class (optional). If a base class is defined, then your new class will act as an extension to that class, inheriting all of the qualities of the base class.
An interface to implement (optional). Interfaces are an advanced topic thus I suggest you forget about these for now until your AS3 and OOP have evolved.
If you wanted to create a class called "Person" within the package classes, then we would end up with:
package classes
{
public class Person
{
// Class qualities here.
}
}
Properties.
Classes can contain properties. Properties are defined using the var keyword. They may belong to one of a number of namespaces (including your own) and are used to hold values that belong to your class. Properties are most commonly found clustered together at the top of your class.
Our Person class may enjoy the properties height and weight:
package classes
{
public class Person
{
// Properties.
public var height:Number = 1.70;
public var weight:Number = 67.5;
}
}
These properties can be accessed via any instance of Person that you create. Each instance will have its own set of these properties.
Class constructors (I believe this is what you're asking about).
Constructors are used to hold logic that should be run as soon as an instance of your class is created. The class constructor has the same name as the class itself. It must be public and it does not return anything. Constructors can accept arguments, which are typically used to pass in references to dependencies for that class or required values.
package classes
{
public class Person
{
// Properties.
public var height:Number = 1.70;
public var weight:Number = 67.5;
// Constructor.
public function Person(height:Number, weight:Number)
{
this.height = height;
this.weight = weight;
}
}
}
Methods.
Methods are used to hold logic that can be run when calling that method. Methods often return values and can accept arguments. Methods can belong to any namespace that you would expect properties to be able to belong to.
We may want to be able to easily determine the BMI of each instance of Person that we create, so we should create a method for that:
package classes
{
public class Person
{
// Properties.
public var height:Number = 170;
public var weight:Number = 65.5;
// Constructor.
public function Person(height:Number, weight:Number)
{
this.height = height;
this.weight = weight;
}
// Determine my BMI and return the result.
public function getBMI():Number
{
return weight / (height * height);
}
}
}
Instances.
Now that we've defined our new class, we can create instances of this class using the new keyword. This can be done from anywhere that can access the Person class, which in this case is anywhere in the project because we've made the class public.
Though the class is public, accessing it from anywhere outside of the package it belongs in will require the use of an import statement. This statement will need to be used within any class that belongs to a different package. The import statement follows the same name used for the package and includes the name of the class you want to include on the end:
import classes.Person;
Once you've imported Person, you can create instances of it and assign them to a variable with different height and weight values:
var marty:Person = new Person(71, 1.76);
var bruce:Person = new Person(96.4, 1.72);
We can then obtain the BMI for each person using their getBMI() method:
trace(marty.getBMI()); // 22.9
trace(bruce.getBMI()); // 32.6
1 You can place classes outside of a package which can be referred to in the same .as file.
2 You can add more source paths, and packages can be relative to that.
The function that have the same name as class is a constructor. In curly brackets is basically part of code that will execute instantly when object will be created. Try to search info about constructors, they exist I think in every object oriented programming language (I may be wrong), so you have a lot of resources.
You can also read about this concept on Wikipedia.
The function that is named the same as the class is the constructor. It's optional, so you can leave it out if you don't need it. A default constructor will be added, which essentially does nothing.
The constructor lets you write code that executes immediately after an instance of the class is created (ie when another bit of code runs new ClassName(). You would typically use it to initialise some variables that are used by the class. Defining a constructor also lets you handle constructor arguments, which other code can pass when they use the new operator.