Skip to main content
Version: Next

Classes

Overview

The 4D language supports the concept of classes. In a programming language, using a class allows you to define an object behaviour with associated properties and functions.

Once a user class is defined, you can instantiate objects of this class anywhere in your code. Each object is an instance of its class. A class can extend another class, and then inherits from its functions and properties (declared and computed).

The class model in 4D is similar to classes in JavaScript, and based on a chain of prototypes.

For example, you could create a Person class with the following definition:

//Class: Person.4dm
Class constructor($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

Function get fullName() -> $fullName : Text
$fullName:=This.firstName+" "+This.lastName

Function sayHello() -> $welcome : Text

$welcome:="Hello "+This.fullName

In a method, creating a "Person":

var $person : cs.Person //object of Person class  
var $hello : Text
$person:=cs.Person.new("John";"Doe")
// $person:{firstName: "John"; lastName: "Doe"; fullName: "John Doe"}
$hello:=$person.sayHello() //"Hello John Doe"

Class files are managed through the 4D Explorer (see Creating classes).

Deleting a class

To delete an existing class, select it in the Explorer and click or choose Move to Trash from the contextual menu.

You can also remove the .4dm class file from the "Classes" folder on your disk.

Class stores

Available classes are accessible from their class stores. Two class stores are available:

  • cs for user class store
  • 4D for built-in class store

cs

cs : Object

ParameterTypeDescription
classStoreObjectUser class store for the project or component

The cs command returns the user class store for the current project or component. It returns all user classes defined in the opened project or component. By default, only project ORDA classes are available.

Example

You want to create a new instance of an object of myClass:

$instance:=cs.myClass.new()

4D

4D : Object

ParameterTypeDescription
classStoreObject4D class store

The 4D command returns the class store for available built-in 4D classes. It provides access to specific APIs such as CryptoKey.

Examples

You want to create a new key in the CryptoKey class:

$key:=4D.CryptoKey.new(New object("type";"ECDSA";"curve";"prime256v1"))

You want to list 4D built-in classes:

 var $keys : Collection
$keys:=OB Keys(4D)
ALERT("There are "+String($keys.length)+" built-in classes.")

Class object

When a class is defined in the project, it is loaded in the 4D language environment. A class is an object itself, of "Class" class. A class object has the following properties and function:

In addition, a class object can reference a constructor object (optional).

A class object itself is a shared object and can therefore be accessed from different 4D processes simultaneously.

Inheritance

If a class inherits from another class (i.e. the Class extends keyword is used in its definition), the parent class is its superclass.

When 4D does not find a function or a property in a class, it searches it in its superclass; if not found, 4D continues searching in the superclass of the superclass, and so on until there is no more superclass (all objects inherit from the "Object" superclass).

Class keywords

Specific 4D keywords can be used in class definitions:

  • Function <Name> to define class functions of the objects.
  • Class constructor to initialize new objects of the class.
  • property to define static properties of the objects with a type.
  • Function get <Name> and Function set <Name> to define computed properties of the objects.
  • Class extends <ClassName> to define inheritance.
  • This and Super are commands that have special features within classes.

Function

Syntax

{local | server} {shared} Function <name>({$parameterName : type; ...}){->$parameterName : type}
// code
note

There is no ending keyword for function code. The 4D language automatically detects the end of a function's code by the next Function keyword or the end of the class file.

Class functions are specific properties of the class. They are objects of the 4D.Function class. In the class definition file, function declarations use the Function keyword followed by the function name.

If the function is declared in a shared class, you can use the shared keyword so that the function could be called without Use...End use structure. For more information, refer to the Shared functions paragraph below.

In the context of a client/server application, the local or server keyword allows you to specify on which machine the function must be executed. These keywords can only be used with ORDA data model functions and shared/session singleton functions. For more information, refer to the local and server functions paragraph below.

The function name must be compliant with object naming rules.

note

Since properties and functions share the same namespace, using the same name for a property and a function of the same class is not allowed (an error is thrown in this case).

tip

Starting the function name with an underscore character ("_") will exclude the function from the autocompletion features in the 4D code editor. For example, if you declare Function _myPrivateFunction in MyClass, it will not be proposed in the code editor when you type in "cs.MyClass. ".

Immediately following the function name, parameters for the function can be declared with an assigned name and data type, including the return parameter (optional). For example:

Function computeArea($width : Integer; $height : Integer)->$area : Integer

Within a class function, the This command is used as the object instance. For example:

Function setFullname($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

Function getFullname()->$fullname : Text
$fullname:=This.firstName+" "+Uppercase(This.lastName)

For a class function, the Current method name command returns: <ClassName>.<FunctionName>, for example "MyClass.myFunction".

In the application code, class functions are called as member methods of the object instance and can receive parameters if any. The following syntaxes are supported:

  • use of the () operator. For example, myObject.methodName("hello")
  • use of a "4D.Function" class member method:
Thread-safety warning

If a class function is not thread-safe and called by a method with the "Can be run in preemptive process" attribute:

  • the compiler does not generate any error (which is different compared to regular methods),
  • an error is thrown by 4D only at runtime.

Parameters

Function parameters are declared using the parameter name and the parameter type, separated by a colon. The parameter name must be compliant with property naming rules. Multiple parameters (and types) are separated by semicolons (;).

Function add($x; $y : Variant; $z : Integer; $xy : Object)
note

If the type is not stated, the parameter will be defined as Variant.

Return value

You declare the return parameter (optional) by adding an arrow (->) and the return parameter definition after the input parameter(s) list, or a colon (:) and the return parameter type only. For example:

Function add($x : Variant; $y : Integer)->$result : Integer
$result:=$x+$y

You can also declare the return parameter by adding only : type and use the return expression (it will also end the function execution). For example:

Function add($x : Variant; $y : Integer): Integer
// some code
return $x+$y

Example 1

property name : Text
property height; width : Integer

// Class: Rectangle
Class constructor($width : Integer; $height : Integer)
This.name:="Rectangle"
This.height:=$height
This.width:=$width

// Function definition
Function getArea()->$result : Integer
$result:=(This.height)*(This.width)
// In a project method

var $rect : cs.Rectangle
var $area : Real

$rect:=cs.Rectangle.new(50;100)
$area:=$rect.getArea() //5000

Example 2

This example uses the return expression:

Function getRectArea($width : Integer; $height : Integer) : Integer
If ($width > 0 && $height > 0)
return $width * $height
Else
return 0
End if

Class constructor

Syntax

// Class: MyClass
{shared} {{session} singleton} Class constructor({$parameterName : type; ...})
// code
note

There is no ending keyword for class constructor function code. The 4D language automatically detects the end of a function's code by the next Function keyword or the end of the class file.

A class constructor function accepts optional parameters and can be used to create and initialize objects of the user class.

When you call the new() function, the class constructor is called with the parameters optionally passed to the new() function.

There can only be one constructor function in a class (otherwise an error is returned). A constructor can use the Super keyword to call the constructor of the super class.

You can create and type instance properties inside the constructor (see example). Alternatively, if your instance properties' values do not depend on parameters passed to the constructor, you can define them using the property keyword.

Using the shared keyword creates a shared class, used to only instantiate shared objects. For more information, refer to the Shared classes paragraph.

Using the singleton keyword creates a singleton, used to create a single instance of the class. A session singleton creates a single instance per session. For more information, refer to the Singleton classes paragraph.

note

ORDA entity classes can also benefit from a Class constructor function. The implementation is similar as for regular classes but with some differences.

Example

// Class: MyClass
// Class constructor of MyClass
Class constructor ($name : Text ; $age : Integer)
This.name:=$name
This.age:=$age
// In a project method
// You can instantiate an object
var $o : cs.MyClass
$o:=cs.MyClass.new("John";42)
// $o = {"name":"John";"age":42}

property

Syntax

property <propertyName>{; <propertyName2>;...}{ : <propertyType>}

The property keyword can be used to declare a property inside a user class. A class property has a name and a type.

Declaring class properties enhances code editor suggestions, type-ahead features and error detection.

Properties are declared for new objects when you call the new() function, however they are not automatically added to objects (they are only added when they are assigned a value).

note

A property is automatically added to the object when it is inititalized in the declaration line.

Property names must be compliant with property naming rules.

note

Since properties and functions share the same namespace, using the same name for a property and a function of the same class is not allowed (an error is thrown in this case).

The property type can be one of the following supported types:

propertyTypeContents
TextText value
DateDate value
TimeTime value
BooleanBoolean value
IntegerLong integer value
RealReal value
PointerPointer value
PicturePicture value
BlobScalar Blob value
CollectionCollection value
VariantVariant value
ObjectObject with default class (4D.Object)
4D.<className>Object of the 4D class name
cs.<className>Object of the user class name
cs.<namespace>.<className>Object of the <namespace> component class name

If you omit the type in the declaration line, the property is created as a variant.

info

The property keyword can only be used in class methods and outside any Function or Class constructor block.

Initializing the property in the declaration line

When declaring a property, you have the flexibility to specify its data type and provide its value in one statement. The supported syntax is:

property <propertyName> { : <propertyType>} := <Propertyvalue>

note

When using this syntax, you cannot declare several properties in the declaration line.

You can omit the type in the declaration line, in which case the type will be inferred when possible. For example:

// Class: MyClass

property name : Text := "Smith"
property age : Integer := 42

property birthDate := !1988-09-29! //date is inferred
property fuzzy //variant

When you initialize a property in its declaration line, it is added to the class object after its instantiation with the new() function but before the constructor is called.

If a class extends another class, the properties of the parent class are instantiated before the properties of the child class.

note

If you initialize a property in its declaration line with an object or a collection in a shared class, the value is automatically transformed into a shared value:

// in a shared class
property myCollection := ["something"]
// myCollection will be a shared collection
// equivalent to:
myCollection := New shared collection("something")

Example

// Class: MyClass

property name : Text
property age : Integer
property color : Text := "Blue"

In a method:

var $o : cs.MyClass
$o:=cs.MyClass.new() //$o:{"color" : "Blue"}
$o.name:="John" //$o:{"color" : "Blue"; "name" : "John"}
$o.age:="Smith" //error with check syntax

Function get and Function set

Syntax

{local | server} {shared} Function get <name>()->$result : type
// code
{local | server} {shared} Function set <name>($parameterName : type)
// code

Function get and Function set are accessors defining computed properties in the class. A computed property is a named property with a data type that masks a calculation. When a computed property value is accessed, 4D substitutes the corresponding accessor's code:

  • when the property is read, the Function get is executed,
  • when the property is written, the Function set is executed.

If the property is not accessed, the code never executes.

note

ORDA entity classes benefit from an extended implementation of computed properties with two additional functions: query and orderBy.

Computed properties are designed to handle data that do not necessary need to be kept in memory. They are usually based upon persistent properties. For example, if a class object contains as persistent property the gross price and the VAT rate, the net price could be handled by a computed property.

In the class definition file, computed property declarations use the Function get (the getter) and Function set (the setter) keywords, followed by the name of the property. The name must be compliant with property naming rules.

Function get returns a value of the property type and Function set takes a parameter of the property type. Both arguments must comply with standard function parameters.

When both functions are defined, the computed property is read-write. If only a Function get is defined, the computed property is read-only. In this case, an error is returned if the code tries to modify the property. If only a Function set is defined, 4D returns undefined when the property is read.

If the functions are declared in a shared class, you can use the shared keyword with them so that they could be called without Use...End use structure. For more information, refer to the Shared functions paragraph below.

In the context of a client/server application, the local or server keyword allows you to specify on which machine the function must be executed. These keywords can only be used with ORDA data model functions and shared/session singleton functions. For more information, refer to the local and server functions paragraph below.

The type of the computed property is defined by the $return type declaration of the getter. It can be of any valid property type.

Assigning undefined to an object property clears its value while preserving its type. In order to do that, the Function get is first called to retrieve the value type, then the Function set is called with an empty value of that type.

note

ORDA entity classes can also benefit from a Class constructor function. The implementation is similar as for regular classes but with some differences.

Example 1

//Class: Person.4dm
property firstName; lastName : Text

Class constructor($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

Function get fullName() -> $fullName : Text
$fullName:=This.firstName+" "+This.lastName

Function set fullName( $fullName : Text )
$p:=Position(" "; $fullName)
This.firstName:=Substring($fullName; 1; $p-1)
This.lastName:=Substring($fullName; $p+1)
//in a project method
$fullName:=$person.fullName // Function get fullName() is called
$person.fullName:="John Smith" // Function set fullName() is called

Example 2

Function get fullAddress()->$result : Object

$result:=New object

$result.fullName:=This.fullName
$result.address:=This.address
$result.zipCode:=This.zipCode
$result.city:=This.city
$result.state:=This.state
$result.country:=This.country

Class extends <ClassName>

Syntax

// Class: ChildClass
Class extends <ParentClass>

The Class extends keyword is used in class declaration to create a user class which is a child of another user class. The child class inherits all functions of the parent class.

Class extension must respect the following rules:

  • A user class cannot extend a built-in class (except 4D.Object and ORDA classes which are extended by default for user classes).
  • A user class cannot extend a user class from another project or component.
  • A user class cannot extend itself.
  • It is not possible to extend classes in a circular way (i.e. "a" extends "b" that extends "a").
  • It is not possible to define a shared user class extended from a non-shared user class.

Breaking such a rule is not detected by the code editor or the interpreter, only the compiler and check syntax will throw an error in this case.

An extended class can call the constructor of its parent class using the Super command.

Example

This example creates a class called Square from a class called Polygon.

//Class: Square

//path: Classes/Square.4dm

Class extends Polygon

Class constructor ($side : Integer)

// It calls the parent class's constructor with lengths
// provided for the Polygon's width and height
Super($side;$side)
// In derived classes, Super must be called before you
// can use 'This'
This.name:="Square"



Function getArea() -> $area : Integer
$area:=This.height*This.width

Class function commands

The following commands have specific features when they are used within class functions:

Super

The Super command allows calls to the superclass, i.e. the parent class of the function. It can be called in the class constructor or in a class function code.

For more details, see the Super command description.

This

The This command returns a reference to the currently processed object. In most cases, the value of This is determined by how a class function is called. Usually, This refers to the object the function was called on, as if the function were on the object.

Example:

//Class: ob

Function f() : Integer
return This.a+This.b

Then you can write in a method:

$o:=cs.ob.new()
$o.a:=5
$o.b:=3
$val:=$o.f() //8

For more details, see the This command description.

OB Class

OB Class ( object ) -> Object | Null

OB Class returns the class of the object passed in parameter.

OB Instance of

OB Instance of ( object ; class ) -> Boolean

OB Instance of returns true if object belongs to class or to one of its inherited classes, and false otherwise.

Shared classes

You can create shared classes. A shared class is a user class that instantiates a shared object when the new() function is called on the class. A shared class can only create shared objects.

Shared classes also support shared functions that can be called without Use...End use structures.

The .isShared property of Class objects allows to know if the class is shared.

info

Creating a shared class

To create a shared class, add the shared keyword before the Class constructor. For example:

	//shared class: Person
shared Class constructor($firstname : Text; $lastname : Text)
This.firstName:=$firstname
This.lastName:=$lastname

//myMethod
var $person := cs.Person.new("John"; "Smith")
OB Is shared($person) // true
cs.Person.isShared //true

Shared functions

If a function defined inside a shared class modifies objects of the class, it should call Use...End use structure to protect access to the shared objects. However, to simplify the code, you can define the function as shared so that it automatically triggers internal Use...End use when executed.

To create a shared function, add the shared keyword before the Function keyword in a shared class. For example:

	//shared class Foo
shared Class constructor()
This.variable:=1

shared Function Bar($value : Integer)
This.variable:=$value //no need to call use/end use
note

If the shared function keyword is used in a non-shared user class, it is ignored.

Singleton classes

A singleton class is a user class that only produces a single instance. For more information on the concept of singletons, please see the Wikipedia page about singletons.

Singletons types

Singletons are useful to define values that need to be available from anywhere in an application, a session, or a process.

4D supports three types of singletons:

  • a process singleton has a unique instance for the process in which it is instantiated,
  • a shared singleton has a unique instance for all processes on the machine.
  • a session singleton is a shared singleton but with a unique instance for all processes in the session. Session singletons are shared within an entire session but vary between sessions. In the context of a client-server or a web application, session singletons make it possible to create and use a different instance for each session, and therefore for each user. Session singletons are particularly appropriate with Qodly applications.
info

Singleton classes are not supported by ORDA-based classes.

The following table indicates the scope of a singleton instance depending on where it was created:

Singleton created onScope of process singletonScope of shared singletonScope of session singleton
4D single-userProcessApplicationApplication or Web/REST session
4D ServerProcess4D Server machineClient/server session or Web/REST session or Stored procedure session
4D remote modeProcess (note: singletons are not synchronized on the twin process)4D remote machine4D remote machine or Web/REST session

Once instantiated, a singleton class (and its singleton) exists as long as a reference to it exists somewhere in the application running on the machine.

Creating and using singletons

You declare singleton classes by adding appropriate keyword(s) before the Class constructor:

  • To declare a (process) singleton class, write singleton Class constructor().
  • To declare a shared singleton class, write shared singleton Class constructor().
  • To declare a session singleton class, write session singleton Class constructor().
note
  • Session singletons are automatically shared singletons (there's no need to use the shared keyword in the class constructor).
  • Singleton shared functions support onHTTPGet keyword.

The class singleton is instantiated at the first call of the cs.<class>.me property. The instantiated class singleton is then always returned when the me property is used.

If you need to instantiate a singleton with parameters, you can also call the new() function. In this case, it is recommended to instantiate the singleton in some code executed at application startup.

The .isSingleton property of Class objects allows to know if the class is a singleton.

The .isSessionSingleton property of Class objects allows to know if the class is a session singleton.

Exposed singleton functions

Shared and session singleton functions support the exposed keyword. An exposed singleton function can be directly called by REST requests. This feature is useful to design Qodly pages calling 4D functions.

Examples

Process singleton

	//class: ProcessTag
singleton Class constructor()
This.tag:=Random

To use the singleton:

	//in a process
var $mySingleton := cs.ProcessTag.me //First instantiation
//$mySingleton.tag = 5425 for example
...
var $myOtherSingleton := cs.ProcessTag.me
//$myOtherSingleton.tag = 5425

	//in another process
var $mySingleton := cs.ProcessTag.me //First instantiation
//$mySingleton.tag = 14856 for example
...
var $myOtherSingleton := cs.ProcessTag.me
//$myOtherSingleton.tag = 14856

Shared singleton

//Class VehicleFactory

property vehicleBuilt : Integer

shared singleton Class constructor()
This.vehicleBuilt := 0 //Number of vehicles built by the factory

shared Function buildVehicle ($type : Text) -> $vehicle : cs.Vehicle

Case of
: $type="car"
$vehicle:=cs.Car.new()
: $type="truck"
$vehicle:=cs.Truck.new()
: $type="sport car"
$vehicle:=cs.SportCar.new()
: $type="motorbike"
$vehicle:=cs.Motorbike.new()
Else
$vehicle:=cs.Car.new()
End case
This.vehicleBuilt+=1

You can then call the cs.VehicleFactory singleton to get a new vehicle from everywhere in the application on your machine with a single line:

$vehicle:=cs.VehicleFactory.me.buildVehicle("truck")

Since the buildVehicle() function modifies the cs.VehicleFactory singleton (by incrementing This.vehicleBuilt) you need to add the shared keyword to it.

Session singleton

In an inventory application, you want to implement an item inventory using session singletons.

//class ItemInventory

property itemList : Collection:=[]

session singleton Class constructor()

shared function addItem($item:object)
This.itemList.push($item)

By defining the ItemInventory class as a session singleton, you make sure that every session and therefore every user has their own inventory. Accessing the user's inventory is as simple as:

//in a user session
$myList := cs.ItemInventory.me.itemList
//current user's item list

local and server

In client/server architecture, local and server keywords allow you to specify where you want the function to be executed: client-side, or server-side. Controlling the execution location is useful for performance reasons or to implement business logic features.

The formal syntax is:

// declare a function to execute on a client in client/server
local Function <functionName>
// declare a function to execute on the server in client/server
server Function <functionName>

local and server keywords are only available for the functions of the following classes:

Overview

Supported functions have a default execution location when no location keyword is used. You can nevertheless insert a local or server keyword to modify the execution location, or to make the code more explicit.

Supported functionsDefault executionwith local keywordwith server keyword
ORDA data modelon ServerThe function is executed on the client if called on the client
Shared or session singletonLocalThe function is executed on the server on the server instance of the singleton.
If there is no instance of the singleton on the server, it is created.

If local and server keywords are used in another context, an error is returned.

note

For a overall description of where code is actually executed in client/server, please refer to this section.

local

In a client/server architecture, the local keyword specifies that the function must be executed on the machine from where it is called.

Reminder

The local keyword is useless for shared or session singleton functions, which are executed locally by default.

By default, ORDA data model functions are executed on the server. It usually provides the best performance since only the function request and the result are sent over the network. However, for optimization reasons, you could want to execute a data model function on client. You can then use the local keyword.

Example: Calculating age

Given an entity with a birthDate attribute, we want to define an age() function that would be called in a list box. This function can be executed on the client, which avoids triggering a request to the server for each line of the list box.

On the StudentsEntity class:

Class extends Entity

local Function age() -> $age: Variant

If (This.birthDate#!00-00-00!)
$age:=Year of(Current date)-Year of(This.birthDate)
Else
$age:=Null
End if

server

In a client/server architecture, the server keyword specifies that the function must be executed on the server side.

Reminder

The server keyword is useless for ORDA data model functions, which are executed on the server by default.

server function parameters and result must be streamable. For example, 4D.Datastore, File handle, or WebServer are non-streamable classes but 4D.File is streamable.

This feature is particularly useful in the context of remote user sessions, allowing you to implement the business logic in a session singleton to share it accross all the processes of the session, thus extending the functionalities of the Session command. In this case, you might want the relevant business logic to be executed on the server so that all the session information is gathered on the server.

By default, shared or session singleton functions are executed locally. Adding the server keyword in the class function definition makes 4D use the singleton instance on the server. Note that this can result of an instantiation of the singleton on the server if no instance exists yet.

For sessions singletons, the function is executed on the server in the corresponding singleton instance, i.e. the instance of the singleton for the current session.

note

If you declare a server Function in a shared singleton, then:

  • you instantiate a singleton S1 on the client (named s1),
  • you run s1.function() on the client.

If no instance of S1 exists on the server at that moment, S1 is instantiated on the server (the constructor is executed), and function() runs on that server instance. As a result, two instances of S1 can coexist (client-side and server-side), with distinct property values. In this case, s1.property is always accessed locally. It cannot be accessed on the server, for example from server-side code using direct dot notation (an error is returned).

Example: Administration singleton

The Administration shared singleton has a "server" function running the Process activity command. This singleton is instantiated on a remote 4D but the function returns the server activity on the server.

  // Administration class

shared singleton Class constructor

// This function is executed on the server
server Function processActivity() : Object
return Process activity


Function localProcessActivity() : Object
return Process activity

Code running on the client:

var $localActivity; $serverActivity : Object
var $administration : cs.Administration

// The Administration singleton is instantiated on the 4D Client
$administration:=cs.Administration.me

// Get processes running on the remote 4D
$localActivity:=$administration.localProcessActivity()

// Get processes and sessions running on 4D Server
$serverActivity:=$administration.processActivity()

Example: Session singleton

You store your users in a Users table and handle a custom authentication. You use a session singleton for the authentication:

// UserSession session singleton class

server Function checkUser($credentials : Object) : Boolean

var $user : cs.UsersEntity
var $result:=False

If ($credentials#Null)
$user:=ds.Users.query("Email === :1"; $credentials.identifier).first()

If (($user#Null) && (Verify password hash($credentials.password; $user.Password)))
Use (Session.storage)
Session.storage.userInfo:=New shared object("userId"; $user.ID)
End use

$result:=True
End if
End if

return $result

To provide the current user to 4D clients, the singleton exposes a user computed property got from the server:

server Function get user() : cs.UsersEntity
return ds.Users.get(Session.storage.userInfo.userId)