Besides the two predefined classes Object and Page, new classes can be defined using the def, defenv, or defclass tags with inherit. Each heitml class has a name and methods. Method definitions can be placed inside class definition. Methods inside a class definition directly belong to that class. They are called nested methods in contrast to the top level methods.
Classes can have a single special method called constructor. The constructor has the same name as the class and it is defined implicitly when the def or defenv tag is used to define the class.
An object of a particular class can be created by calling the constructor method of that class. The constructor method has the same name as the class. For top level classes (classes defined outside any other class definition) the same visibility, calling, and parameter passing rules apply as for top level methods. In addition to a normal method call, a constructor call creates an object of the current class. The constructor can access the object by using the this key word.
<def myclass; inherit object; this.test = "hello"; ? this; /def> <myclass/>
in contrast to that a normal method call does not create a new object but uses the default page object, since top level methods automatically belong to the predefined Page class
<def mymethod; this.test = "hello"; ? this; /def> <mymethod/>
The newly created object can be returned as a function result, as an output parameter, or stored in a global variable.
Inheritance means that all methods that belong (directly or indirectly) to the superclasses (indirectly) belong to the defined class unless they are explicitly overwritten. A method can be overwritten by a method with the same name, in the defined class or in a superclass later in the list of superclasses.
Class based Method Call
Nested methods that (directly or indirectly through inheritance) belong to a class are visible within this class, i.e. in all methods defined in this class (including the constructor). Directly defined methods are visible after the class definition.
Nested methods are only visible inside classes that define or inherit them and inside environment classes.
Visible nested methods can be called using the tag syntax, just as top level methods. Calling a method implicitly passes the current object (i.e. the object that can be accessed by this) to the called method.
<def myclass2 a; inherit object; def printme; ? this; /def; this.me="Hello"+a; printme; /def> <myclass2 a=" World" />
A call always calls the method of the class of the current object with the given name. This feature is known as virtual method call. In fact, not always the same method is called, but the method called depends on class of the current object.
<def mysuper; inherit object; def printme; ? this; /def; def dowork; this.me=this.me+" world"; printme; /def; this.me="Hello "; dowork; /def; def myclass3; inherit mysuper; def printme>This is myclass: <? this.me></def; this.me="Test "; dowork; /def> <mysuper/> <p> <myclass3/>
Please note, that the myclass3 object inherits the dowork method of the mysuper class. The dowork method in turn calls printme. Since printme has been overwritten, the printme method of myclass3 is called when the current object is of class myclass3.
Object Method Call
If x is an object and m a method then x.m() calls the method m of the class x belongs to. This feature is known as virtual method call since the method to be called is not fixed but is variable depending on what class the object x belongs to.
|Expr::=||Expr . MethodName ( ActualParameterList )|
||||Expr [ Expr ] ( ActualParameterList ) .|
<def myclass4; inherit object; def printme><? this></def; return this; /def; x=myclass4(); x.test="Hello"; x.printme()>
Only methods defined with the def tag can be called this way. Methods of an object defined with defenv can be called with the callenv tag however.
Classes with an environment as constructor have the following additional features. Inside the content of the environment the methods of the class are visible. This means inside the content methods can be called directly using the tag syntax. The methods are not visible inside the content of any nested environments. Inside the content the object of the enclosing constructor can be accessed by parent.
<defenv myenv @this ... ; inherit object; def printme; ? this; /def; defbody; /defenv; myenv test="Hello World"; parent.a=5; printme; /myenv>
Classes without Constructors
Using the defclass tag, classes without constructors can be defined. A class defined with defclass either inherits a constructor, or it stays an abstract class.
It is useful to inherit a constructor, if the behavior of a class needs to be change by overriding some methods, without changing the constructor. This is useful for example to override the action routine of a button.
Abstract classes are useful to encapsulate some functionality that can be inherited by other classes. It is not possible to create object for an abstract class, because there is no constructor.
Classes can be nested, i.e. inside a class another class can be defined. The constructor of the nested class has the same visibility as a method designed in the same class. Otherwise the class acts as if it was defined on top level. So methods of the parent class are not visible inside a nested class. The constructor method is partly a constructor of the nested class and partly a method of the enclosing class. It has the same visibility as any other method of the enclosing class and it gets the current object as a parameter. Since the constructor creates a new object however the passed current object is accessible using creator, while this denotes the newly created object.
<defenv myclass5; inherit object; def nested; inherit object; ? this> <\p> < ? creator; /def; this.a=5; nested; /defenv> <myclass5></myclass5>