Introduction To OOP |
Homepage |
I found a tutorial located at http://www.cafeaulait.org/course/week3/ which I think does a great job of explaining OOP. For each topic, I post my comments on the differences between tradional OOP and the AHK equivalent. For each example, I post the original Java code as well as its AHK equivalent.
Each section will start with a link to the respective page on their site. It will open in a different window (or tab, depending on your browser settings). This allows you to read the tutorial page, and then close it to come back here to see my comments and how to apply the idea to AHK OOP. Hopefully, this will teach everyone about OOP, as well as the syntax differences between traditionall OOP and AHK OOP.
Disclaimer: I do not own any of the rights for the listed tutorial. I merely provide a link between a well written tutorial and my comments that show the difference between "traditional" OOP and AHK OOP. The comments below, however, are my original work, and as such, I own the rights. You may copy, link, or otherwise distribute the below material with or without informing me of doing so.
If you wish to contact me with suggestions, feedback, or to simply say thanks, you can email me at crossthei.thet@gmail.com or PM me.
The author of the linked tutorial provides all examples in Java, which is a free, open-source programming language. Even without knowing Java, the tutorial is still an excellent source of information. If you are unfamiliar with Java, don't worry about the syntax of the code, and instead look for the broader idea. My comments are structured so that they can be understood by programmer and non-programmer alike. If there is something you believe could be explained clearer, don't hesitate to email me or PM me.
I've included all the examples used below, including the Car Class used throughout, in a downloadable zip - IntroToOOP.zip.
Note: the + and - are clickable - they will expand and collapse, respectively, the contents of the "folding". If the text beside the + and - isn't a link, clicking the text will also expand / collapse the "folding".
What
is Object Oriented Programming?
Getting Started
Fields and Methods (basics)
Using methods
Constructors
Access Protection
Further Examples
The Node Class is a template class which is provided with the Class library download - it can be modified to create your own classes.
To declare a class, copy the entire contents of Node.ahk to an empty ahk file. Save the file using the name of the class - ex. Node Class (Node.ahk), Car Class (Car.ahk).
Since classes are StdLib compliant, saving them in a library folder will have them automatically included, as needed.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
In traditional OOP, you define fields - these fields contain the object's data. In AHK OOP, you do this via the getBuiltInValues and getUserDefinedValues functions. Since AHK isn't an OOP language, unlike tradional OOP, you cannot refer to a field by name - instead, you access fields using getters and setters.
Declare built-in values (fields) in the getBuiltInValues function and user-defined values (has no traditional OOP equivalent) in the getUserDefinedValues function. Defining fields is part of the Class V2 design. By declaring the fields, you can easily clone, destroy, or compare two Class objects by calling the respective function.
See the list of AHK OOP types for more information on the types, and how to use them.
Java Code:
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
AHK equivalent:
Car_getBuiltInValues()
{
static BuiltInValues
if (BuiltInValues = "")
{
;only done once
BuiltInValues =
(LTrim Comments Join,
String ;licensePlate - e.g. "New York 543 A23"
double ;speed - in kilometers per hour
double ;maxSpeed - in kilometers per hour
)
}
return BuiltInValues
}
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Car
Class:
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
Java Code:
Car c;
c = new Car();
//or
Car c = new Car();
AHK equivalent:
c := Car_new()
When creating an object, AHK OOP differs from traditional OOP in the following ways.
There is no default constructor created.
The Node Class provides the new function - call it to create a new instance of the class.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Since AHK isn't an OOP language, there is no way to access an object's fields via a ".".
Additionally, in AHK OOP, fields cannot be accessed by name. Instead, you interact via the Class' getters and setters.
Car
Class:
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
Java Code:
Car c = new Car();
c.licensePlate = "New York A45 636";
c.speed = 70.0;
c.maxSpeed = 123.45;
//outputs: "New York A45 636 is moving at 70.0 kilometers per hour."
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
AHK equivalent:
c := Car_new()
Car_setLicensePlate(c, "New York A45 636")
Car_setSpeed(c, 70.0)
Car_setMaxSpeed(c, 123.45)
;outputs: "New York A45 636 is moving at 70.0 kilometers per hour."
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Note: you must define the getters and setters - they will be explained later.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
In AHK, there is no main method like in Java. Instead, you
interact with a Class the same way you would with any function or
command.
Car
Class:
class Car {
String licensePlate; // e.g. "New York 543 A23"
double speed; // in kilometers per hour
double maxSpeed; // in kilometers per hour
}
Java Code:
class CarTest {
public static void main(String args[]) {
Car c = new Car();
c.licensePlate = "New York A45 636";
c.speed = 70.0;
c.maxSpeed = 123.45;
//outputs: "New York A45 636 is moving at 70.0 kilometers per hour."
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
}
}
AHK equivalent:
;CarTest.ahk
c := Car_new()
Car_setLicensePlate(c, "New York A45 636")
Car_setSpeed(c, 70.0)
Car_setMaxSpeed(c, 123.45)
;outputs: "New York A45 636 is moving at 70.0 kilometers per hour."
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Note: you must define the getters and setters - they will be explained later.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
In AHK OOP, you can initialize fields in one of the following ways:
Car
Class:
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
}
Java Code:
class CarTest2 {
public static void main(String[] args) {
Car c = new Car();
//outputs: " is moving at 0.0 kilometers per hour."
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
}
}
AHK equivalent:
;CarTest2.ahk
c := Car_new()
;outputs: " is moving at 0.0 kilometers per hour."
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Note: you must define the getters - they will be explained later.
Below are three versions of the Car_new function - any of which will produce the above output. Changes made to Node_new (the template new function) are in red.
Car_new()
{
static licensePlate = "" ; e.g. "New York 543 A23"
static speed = 0.0 ; in kilometers per hour
static maxSpeed = 123.45 ; in kilometers per hour
;this part is identical for all Car constructors
;size of built-in values (4 for licensePlate (String), and 8 for each of the two doubles)
static BuiltInSize = 20
static UserDefinedSize = 0
;Allocate Car info structure
if CarObject := Class_Alloc(4 * UserDefinedSize + BuiltInSize)
{
;Store "Class values" (must be present in ALL classes)
Class_setClass(CarObject, Car_initClass())
Class_setBuiltInSize(CarObject, BuiltInSize)
Class_setUserDefinedSize(CarObject, UserDefinedSize)
;Set Built-in values
;Note: you must define the setters - they will be explained later.
Car_setLicensePlate(CarObject, licensePlate)
Car_setSpeed(CarObject, speed)
Car_setMaxSpeed(CarObject, maxSpeed)
}
return CarObject
}
Car_new(licensePlate = "", speed = 0.0, maxSpeed = 123.45)
{
;this part is identical for all Car constructors
;size of built-in values (4 for licensePlate (String), and 8 for each of the two doubles)
static BuiltInSize = 20
static UserDefinedSize = 0
;Allocate Car info structure
if CarObject := Class_Alloc(4 * UserDefinedSize + BuiltInSize)
{
;Store "Class values" (must be present in ALL classes)
Class_setClass(CarObject, Car_initClass())
Class_setBuiltInSize(CarObject, BuiltInSize)
Class_setUserDefinedSize(CarObject, UserDefinedSize)
;Set Built-in values
;Note: you must define the setters - they will be explained later.
Car_setLicensePlate(CarObject, licensePlate)
Car_setSpeed(CarObject, speed)
Car_setMaxSpeed(CarObject, maxSpeed)
}
return CarObject
}
Note: The only difference between Method 1 and Method 2 is that Method 2 has the fields are parameters.
In some cases, a combination of Method 1 and 2 may be desired. For example, you may have some parameters that have default values (optional parameters), while others have their default value defined inside the new function. Note: initializing a field is completely optional - do so to specify an initial value.
Method 3 (Method 1 + Method 2):
Car_new(licensePlate = "", maxSpeed = 123.45)
{
static speed = 0.0 ; in kilometers per hour
;this part is identical for all Car constructors
;size of built-in values (4 for licensePlate (String), and 8 for each of the two doubles)
static BuiltInSize = 20
static UserDefinedSize = 0
;Allocate Car info structure
if CarObject := Class_Alloc(4 * UserDefinedSize + BuiltInSize)
{
;Store "Class values" (must be present in ALL classes)
Class_setClass(CarObject, Car_initClass())
Class_setBuiltInSize(CarObject, BuiltInSize)
Class_setUserDefinedSize(CarObject, UserDefinedSize)
;Set Built-in values
;Note: you must define the setters - they will be explained later.
Car_setLicensePlate(CarObject, licensePlate)
Car_setSpeed(CarObject, speed)
Car_setMaxSpeed(CarObject, maxSpeed)
}
return CarObject
}
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
When writing methods in AHK (functions), there are some differences from their Java equivalent.
Since AHK doesn't have types, you don't have to specify a return type for the function - just return any value you like (or return none). You can return Class objects like you would any other value - since, internally, they are just numbers.
In AHK OOP, there is no this keyword. See the below
example for other syntax related differences between traditional OOP and
AHK OOP.
Note: a class method is like any other function in AHK. See the AHK function faq for more information.
Car
Class:
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
}
Java Code:
//a member function in the Car Class
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
AHK equivalent:
;a member function in the Car Class
; accelerate to maximum speed
; put the pedal to the metal
Car_floorIt(CarObject){
Car_setSpeed(CarObject, Car_getMaxSpeed(CarObject))
}
Note: you must define the getters and setters - they will be explained later.
Java class functions have the following form:
//a function in the Class ClassName
ReturnType functionName(args...)
{
//your code goes here
}
AHK class functions have the following form:
;a function in the Class ClassName
ClassName_functionName(ClassNameObject, args...)
{
;your code goes here
}
Notice the structure used:
"ClassName_" precedes the function name - this signifies that the function belongs to Class ClassName.
The first argument for a class function is the class object on which to act.
If the function were a static function, it would not have the class object as the first parameter.
;a static function in the Class ClassName
ClassName_functionName(args...)
{
;your code goes here
}
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
You call class methods the same way you would any other functions. Note that the use of the . separator isn't allowed because AHK isn't an OOP language.
Car
Class:
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
}
Java Code:
class CarTest3 {
public static void main(String args[]) {
Car c = new Car();
c.licensePlate = "New York A45 636";
c.maxSpeed = 123.45;
//outputs: "New York A45 636 is moving at 0.0 kilometers per hour."
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
c.floorIt();
//outputs: "New York A45 636 is moving at 123.45 kilometers per hour."
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
}
}
AHK equivalent:
;CarTest3.ahk
c := Car_new()
Car_setLicensePlate(c, "New York A45 636")
Car_setMaxSpeed(c, 123.45)
;outputs: "New York A45 636 is moving at 0.0 kilometers per hour."
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Car_floorIt(c)
;outputs: "New York A45 636 is moving at 123.45 kilometers per hour."
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Note: you must define the getters and setters - they will be explained later.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Note: since AHK isn't an OOP programming language, there is no
this keyword, and as such, the implied this doesn't apply.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Note: In AHK OOP, fields have the same kind of scope as in traditional OOP languages. They are neither global or local - rather, they are member variables. Additionally, since the member variables are accessed only via the Class' getters and setters, they won't conflict with global or local variables.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Below is an example applying this concept.
Passing arguments to a class method is just like passing arguments to any other AHK function. This means that, for example, an argument can be ByRef or optional. See the AHK functions faq for details.
Java Code:
//a member function in the Car Class
void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
AHK equivalent:
;a member function in the Car Class
Car_accelerate(CarObject, deltaV) {
Car_setSpeed(CarObject, Car_getSpeed(CarObject) + deltaV)
if (Car_getSpeed(CarObject) > Car_getMaxSpeed(CarObject)) {
Car_setSpeed(CarObject, Car_getMaxSpeed(CarObject))
}
if (Car_getSpeed(CarObject) < 0.0) {
Car_setSpeed(CarObject, 0.0)
}
}
This example is copied from Passing Arguments to Methods, An Example. The link will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Car
Class:
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
}
Java Code:
class CarTest4 {
public static void main(String[] args) {
Car c = new Car();
c.licensePlate = "New York A45 636";
c.maxSpeed = 123.45;
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
for (int i = 0; i < 15; i++) {
c.accelerate(10.0);
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
}
}
}
AHK equivalent:
;CarTest4.ahk
c := Car_new()
Car_setLicensePlate(c, "New York A45 636")
Car_setMaxSpeed(c, 123.45)
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Loop, 15 {
Car_accelerate(c, 10.0)
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
}
Output:
New York A45 636 is moving at 0.0 kilometers per hour.
New York A45 636 is moving at 10.0 kilometers per hour.
New York A45 636 is moving at 20.0 kilometers per hour.
New York A45 636 is moving at 30.0 kilometers per hour.
New York A45 636 is moving at 40.0 kilometers per hour.
New York A45 636 is moving at 50.0 kilometers per hour.
New York A45 636 is moving at 60.0 kilometers per hour.
New York A45 636 is moving at 70.0 kilometers per hour.
New York A45 636 is moving at 80.0 kilometers per hour.
New York A45 636 is moving at 90.0 kilometers per hour.
New York A45 636 is moving at 100.0 kilometers per hour.
New York A45 636 is moving at 110.0 kilometers per hour.
New York A45 636 is moving at 120.0 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Below is an example applying this concept.
In AHK OOP, setters methods are "wrappers" for setting a field. Like setters in tradional OOP, you can specify constraints on the set value (as done in the setMaximumSpeed setter method).
Car
Class:
class Car {
String licensePlate; // e.g. "New York A456 324"
double speed; // kilometers per hour
double maxSpeed; // kilometers per hour
// setter method for the license plate property
void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
// setter method for the maxSpeed property
void setMaximumSpeed(double maxSpeed) {
if (maxSpeed > 0)
this.maxSpeed = maxSpeed;
else
this.maxSpeed = 0.0;
}
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
}
Java Code:
// setter method for the license plate property
void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
// setter method for the maxSpeed property
void setMaximumSpeed(double maxSpeed) {
if (maxSpeed > 0)
this.maxSpeed = maxSpeed;
else
this.maxSpeed = 0.0;
}
AHK equivalent:
; setter method for the license plate property
Car_setLicensePlate(CarObject, licensePlate) {
;"b1" is the index for the licensePlate field
Class_setString(CarObject, "b1", licensePlate)
}
; setter method for the maxSpeed property
Car_setMaximumSpeed(CarObject, maxSpeed) {
;"b2" is the index for <speed> (since licensePlate is a String - 4 bytes, 1 word)
;"b4" is the index for <maxSpeed> (since speed is a double - 8 bytes, 2 words)
if (maxSpeed > 0)
Class_setValue(CarObject, "b4", maxSpeed, "double")
else
Class_setValue(CarObject, "b4", 0.0, "double")
}
Note: check out the index values faq for details on the index value and it's use.
This example is copied from Using Setter Methods, An Example. The link will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Java Code:
class CarTest5 {
public static void main(String args[]) {
Car c = new Car();
c.setLicensePlate("New York A45 636");
c.setMaximumSpeed(123.45);
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
for (int i = 0; i < 15; i++) {
c.accelerate(10.0);
System.out.println(c.licensePlate + " is moving at " + c.speed +
" kilometers per hour.");
}
}
}
AHK equivalent:
;CarTest5.ahk
c := Car_new()
Car_setLicensePlate(c, "New York A45 636")
Car_setMaximumSpeed(c, 123.45)
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Loop, 15 {
Car_accelerate(c, 10.0)
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
}
Output:
New York A45 636 is moving at 0.0 kilometers per hour.
New York A45 636 is moving at 10.0 kilometers per hour.
New York A45 636 is moving at 20.0 kilometers per hour.
New York A45 636 is moving at 30.0 kilometers per hour.
New York A45 636 is moving at 40.0 kilometers per hour.
New York A45 636 is moving at 50.0 kilometers per hour.
New York A45 636 is moving at 60.0 kilometers per hour.
New York A45 636 is moving at 70.0 kilometers per hour.
New York A45 636 is moving at 80.0 kilometers per hour.
New York A45 636 is moving at 90.0 kilometers per hour.
New York A45 636 is moving at 100.0 kilometers per hour.
New York A45 636 is moving at 110.0 kilometers per hour.
New York A45 636 is moving at 120.0 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Returning a value in AHK is very much like returning a value in any other language. Just remember that since AHK doesn't use types, you don't specify a type for the return. As such, there won't be a compilier error when returning "the wrong type".
Additionally, since, internally, Class objects are just over glorified numbers, you can return a Class object like you would any other value.
Java Code:
String getLicensePlate() {
return this.licensePlate;
}
AHK equivalent:
Car_getLicensePlate(CarObject) {
;"b1" is the index for the licensePlate field
return Class_getString(CarObject, "b1")
}
Note: check out the index values faq for details on the index value and it's use.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Although you cannot return multiple values in AHK, you can specify a parameter as ByRef - you can use it to send back extra results.
Car
Class:
class Car {
String licensePlate = ""; // e.g. "New York 543 A23"
double speed = 0.0; // in kilometers per hour
double maxSpeed = 123.45; // in kilometers per hour
// getter (accessor) methods
String getLicensePlate() {
return this.licensePlate;
}
double getMaxSpeed() {
return this.maxSpeed;
}
double getSpeed() {
return this.speed;
}
// setter method for the license plate property
void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
// setter method for the maxSpeed property
void setMaximumSpeed(double maxSpeed) {
if (maxSpeed > 0)
this.maxSpeed = maxSpeed;
else
this.maxSpeed = 0.0;
}
// accelerate to maximum speed
// put the pedal to the metal
void floorIt() {
this.speed = this.maxSpeed;
}
void accelerate(double deltaV) {
this.speed = this.speed + deltaV;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
if (this.speed < 0.0) {
this.speed = 0.0;
}
}
}
Java Code:
// getter (accessor) methods
String getLicensePlate() {
return this.licensePlate;
}
double getSpeed() {
return this.speed;
}
double getMaxSpeed() {
return this.maxSpeed;
}
AHK equivalent:
; getter (accessor) methods
Car_getLicensePlate(CarObject) {
;"b1" is the index for the licensePlate field
return Class_getString(CarObject, "b1")
}
Car_getSpeed(CarObject) {
;"b2" is the index for the speed field
return Class_getValue(CarObject, "b2", "double")
return this.speed;
}
Car_getMaxSpeed(CarObject) {
;"b4" is the index for the maxSpeed field
return Class_getValue(CarObject, "b4", "double")
}
Note: check out the index values faq for details on the index value and it's use.
This example is copied from Using Getter Methods, An Example. The link will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Java Code:
class CarTest6 {
public static void main(String args[]) {
Car c = new Car();
c.setLicensePlate("New York A45 636");
c.setMaximumSpeed(123.45);
System.out.println(c.getLicensePlate() + " is moving at "
+ c.getSpeed() + " kilometers per hour.");
for (int i = 0; i < 15; i++) {
c.accelerate(10.0);
System.out.println(c.getLicensePlate() + " is moving at "
+ c.getSpeed() + " kilometers per hour.");
}
}
}
AHK equivalent:
;CarTest6.ahk
c := Car_new()
Car_setLicensePlate(c, "New York A45 636")
Car_setMaximumSpeed(c, 123.45)
MsgBox, % Car_getLicensePlate(c) " is moving at "
. Car_getSpeed(c) " kilometers per hour."
Loop, 15 {
Car_accelerate(c, 10.0)
MsgBox, % Car_getLicensePlate(c) " is moving at "
. Car_getSpeed(c) " kilometers per hour."
}
Output:
New York A45 636 is moving at 0.0 kilometers per hour.
New York A45 636 is moving at 10.0 kilometers per hour.
New York A45 636 is moving at 20.0 kilometers per hour.
New York A45 636 is moving at 30.0 kilometers per hour.
New York A45 636 is moving at 40.0 kilometers per hour.
New York A45 636 is moving at 50.0 kilometers per hour.
New York A45 636 is moving at 60.0 kilometers per hour.
New York A45 636 is moving at 70.0 kilometers per hour.
New York A45 636 is moving at 80.0 kilometers per hour.
New York A45 636 is moving at 90.0 kilometers per hour.
New York A45 636 is moving at 100.0 kilometers per hour.
New York A45 636 is moving at 110.0 kilometers per hour.
New York A45 636 is moving at 120.0 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
New York A45 636 is moving at 123.45 kilometers per hour.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Constructors in AHK OOP are limited by a few factors.
AHK doesn't allow multiple functions with the same name (function overloading)
AHK doesn't have types - this means a function's signature is based solely on the number of parameters.
However, since AHK doesn't allow function overloading, there won't be ambiguity between two functions that would, in another language, be overloaded.
Although AHK has these "limitations", this doesn't mean that we can't have multiple constructors. In fact, these "limitations" are great for non-programmers, since they remove any possible confusion concerning which function will be called.
Although many classes specify only one constructor, you can have multiple constructors in a Class. Do this by creating multiple functions, each of which instantiate a new class object (see the examples below).
Note: the built-in size is the same regardless how it is created. This means that each constructor must specify the same built-in size.
In each of the following examples, for the AHK equivalent, the changes from the Node_new template constructor are in red.
In the AHK equivalent, notice how all the "work" is done at the beginning of the constructor - this makes it easy to change at a later time. Note: the bulk of the function (located at the end) remains identical between all Car constructors.
no-arg
constructor:
Java Code:
Car() {
this.licensePlate = "";
this.speed = 0.0;
this.maxSpeed = 120.0;
}
AHK equivalent:
Car_new0()
{
;define values here
;Note: since the values don't change, we can define them as static.
static licensePlate = ""
static speed = 0.0
static maxSpeed = 120.0
;this part is identical for all Car constructors
;size of built-in values (4 for licensePlate (String), and 8 for each of the two doubles)
static BuiltInSize = 20
static UserDefinedSize = 0
;Allocate Car info structure
if CarObject := Class_Alloc(4 * UserDefinedSize + BuiltInSize)
{
;Store "Class values" (must be present in ALL classes)
Class_setClass(CarObject, Car_initClass())
Class_setBuiltInSize(CarObject, BuiltInSize)
Class_setUserDefinedSize(CarObject, UserDefinedSize)
;Set Built-in values
Car_setLicensePlate(CarObject, licensePlate)
Car_setSpeed(CarObject, speed)
Car_setMaxSpeed(CarObject, maxSpeed)
}
return CarObject
}
3-arg
constructor (Car_new):
Java Code:
Car(String licensePlate, double speed, double maxSpeed) {
//difference 1 (this changes in the AHK equivalent)
this.licensePlate = licensePlate;
this.speed = speed;
//difference 2 (this changes in the AHK equivalent)
if (maxSpeed > 0)
this.maxSpeed = maxSpeed;
else
this.maxSpeed = 0.0;
if (speed > this.maxSpeed)
this.speed = this.maxSpeed;
//difference 3 (this changes in the AHK equivalent)
if (speed < 0)
this.speed = 0.0;
else
this.speed = speed;
}
AHK equivalent:
Car_new(licensePlate, speed, maxSpeed)
{
;define values here
;difference 1 (explanation)
/*
omitted - the values will be stored in the object via the setters
(see code at the end of the function)
this.licensePlate = licensePlate;
this.speed = speed;
*/
;difference 2 (explanation)
/*
The first case can be omitted
- the value will be stored at the end of the function (via its setter).
if (maxSpeed > 0)
this.maxSpeed = maxSpeed;
else
this.maxSpeed = 0.0;
*/
if (maxSpeed <= 0)
maxSpeed := 0.0
if (speed > maxSpeed)
speed := maxSpeed;
;difference 3 (explanation)
/*
The second case can be omitted
- the value will be stored at the end of the function (via its setter).
if (speed < 0)
this.speed = 0.0;
else
this.speed = speed;
*/
if (speed < 0)
speed := 0.0
;this part is identical for all Car constructors
;size of built-in values (4 for licensePlate (String), and 8 for each of the two doubles)
static BuiltInSize = 20
static UserDefinedSize = 0
;Allocate Car info structure
if CarObject := Class_Alloc(4 * UserDefinedSize + BuiltInSize)
{
;Store "Class values" (must be present in ALL classes)
Class_setClass(CarObject, Car_initClass())
Class_setBuiltInSize(CarObject, BuiltInSize)
Class_setUserDefinedSize(CarObject, UserDefinedSize)
;Set Built-in values
Car_setLicensePlate(CarObject, licensePlate)
Car_setSpeed(CarObject, speed)
Car_setMaxSpeed(CarObject, maxSpeed)
}
return CarObject
}
2-arg
constructor:
Java Code:
Car(String licensePlate, double maxSpeed) {
//difference 1 (this changes in the AHK equivalent)
this.licensePlate = licensePlate;
//difference 2 (this changes in the AHK equivalent)
this.speed = 0.0;
//difference 3 (this changes in the AHK equivalent)
if (maxSpeed > 0)
this.maxSpeed = maxSpeed;
else
this.maxSpeed = 0.0;
}
AHK equivalent:
Car_new2(licensePlate, maxSpeed)
{
;define values here
;difference 1 (explanation)
/*
omitted - the value will be stored in the object via its setter
(see code at the end of the function)
this.licensePlate = licensePlate;
*/
;difference 2 (explanation)
/*
Since the value doesn't change, we can define it as static.
*/
static speed = 0.0
;difference 3 (explanation)
/*
The first case can be omitted
- the value will be stored at the end of the function (via its setter).
if (maxSpeed > 0)
this.maxSpeed = maxSpeed;
else
this.maxSpeed = 0.0;
*/
if (maxSpeed <= 0)
maxSpeed := 0.0
;this part is identical for all Car constructors
;size of built-in values (4 for licensePlate (String), and 8 for each of the two doubles)
static BuiltInSize = 20
static UserDefinedSize = 0
;Allocate Car info structure
if CarObject := Class_Alloc(4 * UserDefinedSize + BuiltInSize)
{
;Store "Class values" (must be present in ALL classes)
Class_setClass(CarObject, Car_initClass())
Class_setBuiltInSize(CarObject, BuiltInSize)
Class_setUserDefinedSize(CarObject, UserDefinedSize)
;Set Built-in values
Car_setLicensePlate(CarObject, licensePlate)
Car_setSpeed(CarObject, speed)
Car_setMaxSpeed(CarObject, maxSpeed)
}
return CarObject
}
Note: although you could name the constructor whatever you want, for constency, the commonly used constructor should be called new. Any additional constructors should have new followed by the argument count. Any optional parameters should be included in this argument count. For example, instead of Car_new, the function name could have been Car_new3.
This standard should be sufficient for most cases. If a need arises for having multiple constructors that have the same argument count, please post on the Class Library - help thread. At this time, the standard will be updated to accomodate this scenario.
Note: when specifying constraints for a ByRef parameter, if you don't want to change the variable's original value, copy the argument to a local variable and modify this local copy. Remember to also change the variable name in the respective setter (at the end of the constructor).
2-arg constructor:
Car_new2(licensePlate, ByRef maxSpeed)
{
;define values here
static speed = 0.0
;copy the value before use - so not to modify the original
;(remember to change the variable name in the respective setter)
this_maxSpeed := maxSpeed
if (this_maxSpeed <= 0)
this_maxSpeed := 0.0
;this part is identical for all Car constructors
;size of built-in values (4 for licensePlate (String), and 8 for each of the two doubles)
static BuiltInSize = 20
static UserDefinedSize = 0
;Allocate Car info structure
if CarObject := Class_Alloc(4 * UserDefinedSize + BuiltInSize)
{
;Store "Class values" (must be present in ALL classes)
Class_setClass(CarObject, Car_initClass())
Class_setBuiltInSize(CarObject, BuiltInSize)
Class_setUserDefinedSize(CarObject, UserDefinedSize)
;Set Built-in values
Car_setLicensePlate(CarObject, licensePlate)
Car_setSpeed(CarObject, speed)
Car_setMaxSpeed(CarObject, this_maxSpeed)
}
return CarObject
}
In most cases, if a class has more than one constructor, you can create a function that performs the "core" constructing, and add to it. For example, in the Array class, Array_new performs the "core" constructing. If you use the Array_new1 constructor, it calls Array_new and modifies to the returned Array object.
Likewise, the 3-arg car constructor does the "core" constructing. You can, as was done in the included Car.ahk, use the 3-arg constructor in the other two constructors to simplify the code and make it easier to modify later.
Check out Car.ahk for how this was done for the Car constructor and Array.ahk for how it was done in Arrays. The Node_new function has been modified with this design in mind.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Java Code:
class CarTest7 {
public static void main(String args[]) {
Car c = new Car("New York A45 636", 123.45);
System.out.println(c.getLicensePlate() + " is moving at " + c.getSpeed() +
" kilometers per hour.");
for (int i = 0; i < 15; i++) {
c.accelerate(10.0);
System.out.println(c.getLicensePlate() + " is moving at " + c.getSpeed()
+ " kilometers per hour.");
}
}
}
AHK equivalent:
;CarTest7.ahk
c := Car_new2("New York A45 636", 123.45)
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
Loop, 15 {
Car_accelerate(c, 10.0)
MsgBox, % Car_getLicensePlate(c) " is moving at " Car_getSpeed(c)
. " kilometers per hour."
}
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
The notion of constraints carries over to AHK OOP.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
The above link shows the complete class. See the included Car.ahk for the AHK version.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
In AHK OOP, access protection is built-in. This is because there is no direct access to fields, and instead they are accessed via the Class' getters and setters.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
The above link shows the Car Class (as it "would probably be written in practice"). It is functionally equivalent to the previously refered to complete class.
The above linked code specifies each function's access (public, private, protected, or default) Since AHK doesn't have such access protection, there is no AHK equivalent. Instead, you should document which functions should and should not be used by the user of your class.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
The above example shows the compile-time errors that occur in Java due to access protection. The errors result in accessing private fields from outside the class. Since AHK doesn't have this kind of access protection, no such error would result.
In Java, you usually specify a class' fields as non-public (e.g. private) to prevent other classes from modifying the fields directly. By forcing the fields to be accessed from a getter/setter, there is a guarentee that any constraints on the values are upheld.
In AHK OOP, there is no direct access to the class' fields; instead, all accesses are via the class' getters and setters. This means that this access protection still exists - indirectly and built-in.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
The above tutorial page explains the differences between the different "access levels": public, private, protected, and default. There is no AHK equivalent.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
The above mentioned benefits carry over to AHK OOP.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
The code refered to above demonstrates how you can change the implementation of a class without affecting the user of the class. As long as the interface (how the user interacts with the class) remains the same, internal changes won't affect existing code.
Read the respective tutorial page first. It will open in a new window (or tab, depending on your browser settings) - simply close it to come back here, when done.
Note: AHK doesn't have such access protection.
In the below examples, the tutorial page only provides questions one might ask when designing each of the classes (there is no code). These are the same questions that you should ask when creating your own classes.
Questions you should ask when creating a Money Class.
Questions you should ask when creating an Angle Class.
Questions you should ask when creating a Complex Number Class.
Questions you should ask when creating a course registration program.
Questions you should ask when creating a genealogy database.