Classes#
A class is definition of a data type. A class can contain fields, constants, methods, properties, and signals. Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.
The simplest class declaration looks like this:
1class ClassName {
2}
As class types support inheritance, you can specify a base class you want to derive from. A derived class is-a superclass. It gets access to some of its methods etc. It can always be used in place of a and so on….
No classes can have multiple base classes, however GObject subclasses may implement multiple interfaces. By implementing an interface, a classed type has an is-a relationship with an interface. Whenever an instance of that interface is expected, an instance of this class will do.
Types of class#
Vala supports three different types of class:
GObject subclasses are any classes derived directly or indirectly from GLib.Object. This is the most powerful type of class, supporting all features described in this page. This means signals, managed properties, interfaces and complex construction methods, plus all features of the simpler class types.
Fundamental GType classes are those either without any superclass or that don’t inherit at any level from GLib.Object. These classes support inheritance, interfaces, virtual methods, reference counting, unmanaged properties, and private fields. They are instantiated faster than GObject subclasses but are less powerful - it isn’t recommended in general to use this form of class unless there is a specific reason to.
Compact classes, so called because they use less memory per instance, are the least featured of all class types. They are not registered with the GType system and do not support reference counting, virtual methods, or private fields. They do support unmanaged properties. Such classes are very fast to instantiate but not massively useful except when dealing with existing libraries. They are declared using the Compact attribute on the class, See
Any non-compact class can also be defined as abstract. An abstract class cannot be instantiated and is used as a base class for derived classes.
Types of class members#
There are three fundamentally different types of class members, instance, class and static.
Instance members are held per instance of the class. That is, each instance has its own copies of the members in its own instance scope. Changes to instance fields will only apply to that instance, calling instance methods will cause them to be executed in the scope of that instance.
Class members are shared between all instances of a class. They can be accessed without an instance of the class, and class methods will execute in the scope of the class.
Static members are shared between all instances of a class and any sub-classes of it. They can be accessed without an instance of the class, and static methods will execute in the scope of the class.
The distinction between class and static members is not common to other object models. The essential difference is that a sub-class will receive a copy of all its base classes’ class members. This is opposed to static members, of which there is only one copy - sub classes access can their base classes’ static members because they are automatically imported into the class’ scope.
Class scope#
Class scope is more complicated than other scopes, but conceptually the same. A class has a scope, which consists of its static and class members, as describe above. When an instance of the class is created, it is given its own scope, consisting of the defined instance members, with the class’ scope as its parent scope.
Within the code of a class, the instance and class scopes are
automatically searched as appropriate after the local scope, so no
qualification is normally required. When there is a conflict with a name
in the local scope, the this
scope can be used, for example:
1class ClassName {
2 int field_name;
3 void function_name(field_name) {
4 this.field_name = field_name;
5 }
6}
When a name is defined in a class which conflicts with one in a
subclass, the base
scope can be used, to refer to the scope of the
subclass.
Class member visibility#
All class members have a visibility. Visibility is declared using the following mutually exclusive modifiers:
class-member-visibility-modifier:
private
protected
internal
public
This defines whether the member is visible to code in different locations:
“private” asserts that the member will only be visible to code that is within this class declaration
“protected” asserts that the member will be visible to any code within this class, and also to any code that is in a subclass of this class
“internal” asserts that the member should be visible to any code in the project, but excludes the member from the public API of a shared object
“public” asserts that the member should be visible to any code, including the public API of a shared object
Note
C Note
A field or method’s protected status cannot be enforced in the C translation of a Vala library.
Class declaration#
class-declaration:
[ access-modifier ] class qualified-class-name [ inheritance-list ] { [ class-members ] }
qualified-class-name:
[ qualified-namespace-name . ] class-name
class-name:
identifier
inheritance-list:
: superclasses-and-interfaces
superclasses-and-interfaces:
( qualified-class-name | qualified-interface-name ) [ , superclasses-and-interfaces ]
class-members:
class-member [ class-members ]
class-member:
class-creation-method-declaration
class-constructor-declaration
class-destructor-declaration
class-constant-declaration
class-delegate-declaration
class-enum-declaration
class-instance-member
class-class-member
class-static-member
inner-class-declaration
class-constructor-declaration:
class-instance-constructor-declaration
class-class-constructor-declaration
class-static-constructor-declaration
class-instance-member:
class-instance-field-declaration
class-instance-method-declaration
class-instance-property-declaration
class-instance-signal-declaration
class-class-member:
class-class-field-declaration
class-class-method-declaration
class-class-property-declaration
class-static-member:
class-static-field-declaration
class-static-method-declaration
class-static-property-declaration
inner-class-declaration:
[ access-modifier ] class class-name [ inheritance-list ] { [ class-members ] }
In Vala, a class must have either one or zero superclasses, where have zero superclasses has the result described in Types of class section. A class must meet all the prerequisites defined by the interfaces it wishes to implement, by implementing prerequisite interfaces or inheriting from a particular class. This latter requirement means it is potentially possible to have two interfaces that cannot be implemented by a single class.
Note
Note: Interfaces are only supported for GType classes. Compact classes have access only to a limited form of inheritance, whereby they may inherit from exactly one or zero other compact classes.
When declaring which class, if any, a new class subclasses, and which interfaces it implements, the names of those other classes or interfaces can be qualified relative to the class being declared. This means that, for example, if the class is declared as “class foo.Bar” (class “Bar” in namespace “foo”) then it may subclass class “Base” in namespace “foo” simply with “class foo.Bar : Base”.
If an access modifier for the class is not given, the default “internal” is used.
It is possible to declare a class definition to be “abstract.” An abstract class is one they may not be instantiated, instead it first be subclassed by a non-abstract (“concrete”) class. An abstract class declaration may include abstract class instance members. These act as templates for methods or properties that must be implemented in all concrete subclasses of the abstract class. It is thus guaranteed that any instance of the abstract class (which must be in fact an instance of a concrete subclass) will have a method or property as described in the abstract class definition.
abstract-class-declaration:
[ access-modifier ] abstract class qualified-class-name [ inheritance-list ] { [ abstract-class-members ] }
abstract-class-members:
class-members
class-instance-abstract-method-declaration
class-instance-abstract-property-declaration
Controlling instantiation#
When a class is instantiated, data might be required from the user to set initial properties. To define which properties should be or can be set at this stage, the class declaration should be written as:
1class ClassName : GLib.Object {
2
3 public ClassName() {
4 }
5
6 public ClassName.with_some_quality (Property1Type property1value) {
7 this.property1 = property1value;
8 }
9}
This example allows the ClassName
class to be instantiated either
setting no properties, or setting the property. The convention is to
name constructors as “with_” and then a description of what the extra
properties will be used for, though following this is optional.
class-creation-method-declaration:
[ class-member-visibility-modifier ] class-name [ . creation-method-name ] ( param-list ) { construction-assignments }
class-name:
identifier
creation-method-name:
identifier
construction-assignments:
this . property-name = param-name ;
class-name must be the same as the name of the class. If a creation
method is given an extra name, this name is also used with instantiating
the class, using the same syntax as for declaring the method, e.g.
var a = new Button.with_label ("text")
.
If the property being set is construct type then assignment is made before construction, else afterwards.
Any number of these are allowed, but only one with each name (including null name.)
Note
Note:
For a GObject derived class, only properties may be set at this stage in construction, no other processing can be done at this time.
Construction#
Note
Note:
Construction only follows this process in GObject derived classes.
During instantiation, after construction properties have been set, a
series of blocks of code are executed. This is the process that prepares
the instance for use. There are three types of construct
blocks that
a class may define:
class-instance-constructor-declaration:
construct { statement-list }
Code in this block is executed on every instance of the class that is instantiated. It is run after construction properties have been set.
class-class-constructor-declaration:
class construct { statement-list }
This block will be executed once at the first use of its class, and once at the first use of each subclass of this class.
class-static-constructor-declaration:
static construct { statement-list }
The first time that a class, or any subclass of it, is instantiated, this code is run. It is guaranteed that this code will run exactly once in a program where such a class is used.
The order of execution for constructors:
class-instance-destructor-declaration:
~ class-name ( ) { statement-list }
Destruction here. When does it happen? And when for each type of class?
Class fields#
Fields act as variable with a scope of either the class or a particular instance, and therefore have names and types in the same way. Basic declarations are as:
class-instance-field-declaration:
[ class-member-visibility-modifier ] qualified-type-name field-name [ = expression ] ;
class-class-field-declaration:
[ class-member-visibility-modifier ] class qualified-type-name field-name [ = expression ] ;
class-static-field-declaration:
[ class-member-visibility-modifier ] static qualified-type-name field-name [ = expression ] ;
Initial values are optional. FIXME: how much calculation can be done here? what are the defaults?
Note
Note Initial values are only allowed in GObject derived classes.
Class constants#
Constants defined in a class are basically the same as those defined in a namespace. The only difference is the scope and the choice of visibilities available.
class-constant-declaration:
[ class-member-visibility-modifier ] const qualified-type-name constant-name = expression ;
Class methods#
Class methods are methods bound to a particularly class or class instance, i.e. they are executed within the scope of that class or class instance. They are declared the same way as other methods, but within the declaration of a class.
The same visibility modifiers can be used as for fields, although in this case they refer to what code can call the methods, rather than who can see or change values.
The static
modifier is applicable to methods also. A static method
is independent of any instance of the class. It is therefore only in the
class scope, and may only access other static
members.
class-instance-method-declaration:
[ class-member-visibility-modifier ] [ class-method-type-modifier ] return-type method-name ( [ params-list ] ) method-contracts [ throws exception-list ] { statement-list }
class-class-method-declaration:
[ class-member-visibility-modifier ] class return-type method-name ( [ params-list ] ) method-contracts [ throws exception-list ] { statement-list }
class-static-method-declaration:
[ class-member-visibility-modifier ] static return-type method-name ( [ params-list ] ) method-contracts [ throws exception-list ] { statement-list }
class-method-type-modifier:
virtual
override
Methods can be virtual, as described in Object oriented programming. Methods in Vala classes are not virtual automatically, instead the “virtual” modifier must be used when it is needed. Virtual methods will only chain up if overridden using the override keyword.
Vala classes may also define abstract methods, by writing the declaration with the “abstract” modifier and replacing the method body with an empty statement “;”. Abstract methods are not true methods, as they do not have an associated statement block, and so cannot be invoked. Abstract methods can only exist in abstract classes, and must be overridden in derived classes. For this reason an abstract method is always virtual. The purpose of an abstract method is to define methods that all non-abstract subclasses of the current definition must implement, it is therefore always allowable to invoke the method on an instance of the abstract class, because it is required that that instance must in fact be of a non-abstract subclass.
class-instance-abstract-method-declaration:
[ class-member-visibility-modifier ] abstract return-type method-name ( [ params-list ] ) method-contracts [ throws exception-list ] ;
Note
Note
Virtual methods are not available to compact classes.
Properties#
Note
Development Note:
Class and static properties are not yet supported in current Vala releases.
Note
Note
Fully managed properties are only available to GObject derived classes - these are properties that can be set dynamically (by providing the property name at runtime) and can have attached metadata, as is often used in the GTK+ and GNOME libraries. The other class types can have unmanaged properties, which appear similar when using Vala, but are actually implemented using simple methods.
Properties are an enhanced version of fields. They allow custom code to be called whenever the property is retrieved or assigned to, but may be treated as fields by external Vala code. Properties also function like methods to some extent, and so can be defined as virtual and overridden in subclasses. Since they are also allowed in interfaces, they allow interfaces to declare data members that implementing classes must expose (see Interfaces.)
Declaration#
class-instance-property-declaration:
[ class-member-visibility-modifier ] [ class-method-type-modifier ] qualified-type-name property-name { accessors [ default-value ] } ;
class-instance-abstract-property-declaration:
[ class-member-visibility-modifier ] abstract qualified-type-name property-name { automatic-accessors } ;
class-class-property-declaration:
[ class-member-visibility-modifier ] class qualified-type-name property-name { accessors [ default-value ] } ;
class-static-property-declaration:
[ class-member-visibility-modifier ] static qualified-type-name property-name { accessors [ default-value ] } ;
property-name:
identifier
accessors:
automatic-accessors
[ getter ] [ setter ] [ property-constructor ]
automatic-accessors:
[ automatic-getter ] [ automatic-setter ] [ automatic-property-constructor ]
automatic-getter:
[ class-member-visibility-modifier ] get ;
automatic-setter:
[ class-member-visibility-modifier ] set [ construct ] ;
automatic-property-constructor:
[ class-member-visibility-modifier ] construct ;
get-accessor:
[ class-member-visibility-modifier ] get { statement-list }
set-accessor:
[ class-member-visibility-modifier ] set [ construct ] { statement-list }
property-constructor:
[ class-member-visibility-modifier ] construct { statement-list }
default-value:
default = expression ;
Execute Code on Setting/Getting Values#
Properties can either be declared with code that will perform particular actions on get and set, or can simply declare which actions are allowed and allow Vala to implement simple get and set methods. This second pattern (automatic property) will result in fields being added to the class to store values that the property will get and set. If either get or set has custom code, then the other must either be also written in full, or omitted altogether.
When a value is assigned to a property, the set block is invoked, with a parameter called value of the same type as the property. When a value is requested from a property, the get block is invoked, and must return an instance of the same type of the property.
Construct / Set Construct Block#
A property may have zero or one construct blocks. This means either a set construct block or a separate construct block. If this is the case that then the property becomes a construct property, meaning that if it is set in creation method, it will be set (using the construct block, as opposed to any simple set block, where there is a distinction) before class construct blocks are called.
Notify Changes Signals#
Managed properties may be annotated with Notify, See Attributes. This will cause the class instance to emit a notify signal when the property has been assigned to.
Virtual Properties#
Instance properties can be defined virtual with the same semantics as for virtual methods. If in an abstract class, an instance property can be defined as abstract. This is done using the “abstract” keyword on a declaration that is otherwise the same as an automatic property. It is then the responsibility of derived classes to implement the property by providing get or set blocks as appropriate. An abstract property is automatically virtual.
Abstract Properties#
As with methods, it is possible to declare abstract properties. These have much the same semantics as abstract methods, i.e. all non-abstract subclasses will have to implement properties with at least the accessors defined in the abstract property. Any set construct or construct accessor must be defined too in non-abstract classes and use override.
class-instance-abstract-property-declaration:
[ class-member-visibility-modifier ] abstract qualified-type-name property-name { automatic-accessors } ;
Signals#
Note
Note
Signals are only available to GObject derived classes.
Signals are a system allowing a classed-type instance to emit events which can be received by arbitrary listeners. Receiving these events is achieved by connecting the signal to a handler, for which Vala has a specific syntax. Signals are integrated with the GLib MainLoop system, which provides a system for queueing events (i.e. signal emissions,) when needed - though this capability is not needed non-threaded applications.
class-instance-signal-declaration:
[ class-member-visibility-modifier ] [ class-method-type-modifier ] signal return-type signal-name ( [ params-list ] ) ;
signal-name:
identifier
Signals may also provide an extra piece of information called a signal detail. This is a single string, which can be used as an initial hint as to the purpose of the signal emission. In Vala you can register that a signal handler should only be invoked when the signal detail matches a given string. A typical use of signal details is in GObject’s own “notify” signal, which says that a property of an object has changed - GObject uses the detail string to say which property has been changed.
To assign a handler to a signal, (or register to receive this type of event from the instance), use the following form of expression:
signal-connection-expression:
qualified-signal-name [ signal-detail ] += signal-handler
qualified-signal-name:
[ qualified-namespace-name . ] variable-identifier . signal-name
signal-detail:
[ expression ]
signal-handler:
expression
qualified-method-name
lambda-expression
This expression will request that the signal handler given be invoked whenever the signal is emitted. In order for such a connection expression to be legal, the handler must have the correct signature. The handler should be defined to accept as parameters the same types as the signal, but with an extra parameter before. This parameter should have the type of the class in which the signal is declared. When a signal is emitted all handlers are called with this parameter being the object by which the signal was emitted.
The time that an arbitrary expression is acceptable in this expression is when that expression evaluates to an instance of a delegate type, i.e. to a method that is a legal handler for the signal. For details on delegates, see Delegates. For details on lambda expressions see Lambdas.
Note that optional signal detail should be directly appended to the
signal name, with no white space, e.g. o.notify["name"] += ...
It is also possible to disconnect a signal handler using the following expression form:
signal-disconnection-expression:
qualified-signal-name [ signal-detail ] -= connected-signal-handler
connected-signal-handler:
expression
qualified-method-name
Note that you cannot disconnect a signal handler which was defined inline as a lambda expression and then immediately connected to the signal. If this is the effect you really need to achieve, you must assign the lambda expression to an identifier first, so that the lambda can be referred to again at a later time.
Class enums#
Enums defined in a class are basically the same as those defined in a namespace. The only difference is the scope and the choice of visibilities available. See Enumerated types (Enums).
class-enum-declaration:
[ class-member-visibility-modifier ] enum enum-name { [ enum-members ] }
Class delegates#
Delegates defined in a class are basically the same as those defined in a namespace. The only difference is the scope and the choice of visibilities available. See Delegates.
class-delegate-declaration:
[ class-member-visibility-modifier ] return-type delegate delegate-name ( method-params-list ) ;
Examples#
Demonstrating…
1// ...
Using Properties#
For more examples see: Samples for Class Properties
Virtual Properties#
1namespace Properties {
2 class Base : Object {
3 protected int _number;
4 public virtual int number {
5 get {
6 return this._number;
7 }
8 set {
9 this._number = value;
10 }
11 }
12 }
13
14 /*
15 * This class just use Base class default handle
16 * of number property.
17 */
18 class Subclass : Base {
19 public string name { get; set; }
20 }
21
22
23 /**
24 * This class override how number is handle internally.
25 *
26 */
27 class ClassOverride : Base {
28 public override int number {
29 get {
30 return this._number;
31 }
32 set {
33 this._number = value * 3;
34 }
35 }
36
37 public static int main (string[] args) {
38 stdout.printf ("Implementing Virtual Properties...
39");
40 var bc = new Base ();
41 bc.number = 3;
42 stdout.printf ("Class number = '" + bc.number.to_string () + "'
43");
44 var sc = new Subclass ();
45 sc.number = 3;
46 stdout.printf ("Class number = '" + sc.number.to_string () + "'
47");
48 var co = new ClassOverride ();
49 co.number = 3;
50 stdout.printf ("Class number = '" + co.number.to_string () + "'
51");
52 return 0;
53 }
54 }
55}
Abstract Properties#
1namespace Properties {
2 abstract class Base : Object {
3 public abstract string name { get; set construct; }
4
5 construct {
6 this.name = "NO_NAME";
7 }
8 }
9
10 class Subclass : Base {
11 private string _name;
12
13 public override string name {
14 get {
15 return this._name;
16 }
17
18 set construct {
19 this._name = value;
20 }
21 }
22
23 /* This class have a default constructor that initializes
24 * name as the construct block on Base, and a .with_name()
25 * constructor where the user can set class derived name
26 * property.
27 */
28 public Subclass.with_name (string name) {
29 Object (name:name);
30 this._name = name;
31 }
32
33 public static int main (string[] args) {
34 stdout.printf ("Implementing Abstract Properties...
35");
36 var sc = new Subclass.with_name ("TEST_CLASS");
37 stdout.printf ("Class name = '" + sc.name + "'
38");
39 var sc2 = new Subclass ();
40 stdout.printf ("Class name = '" + sc2.name + "'
41");
42 return 0;
43 }
44 }
45}
Compile and run using:
# valac source.vala
# ./source
Using signals#
1public class Test : Object {
2 public signal void test (int data);
3}
4
5delegate void TestHandler (Test t, int data);
6
7public static void main (string[] args) {
8
9 Test t = new Test();
10
11 TestHandler h = (t, data) => {
12 stdout.printf("Data: %d
13", data);
14 };
15
16 t.test (1);
17 t.test.connect (h);
18 t.test (2);
19 t.test.disconnect (h);
20 t.test (3);
21}