3.8. Interfaces¶
A class in Vala may implement any number of interfaces. Each interface is a type, much like a class, but one that cannot be instantiated. By “implementing” one or more interfaces, a class may declare that its instances are also instances of the interface, and therefore may be used in any situation where an instance of that interface is expected.
The procedure for implementing an interface is the same as for inheriting from classes with abstract methods in - if the class is to be useful it must provide implementations for all methods that are described but not yet implemented.
A simple interface definition looks like:
public interface ITest : GLib.Object {
public abstract int data_1 { get; set; }
public abstract void method_1();
}
This code describes an interface “ITest
” which requires GLib.Object as parent of the implementor class and contains two members. “data_1” is a property, as described above, except that it is declared abstract
. Vala will therefore not implement this property, but instead require that classes implementing this interface have a property called “data_1” that has both get
and set
accessors - it is required that this be abstract
as an interface may not have any data members. The second member “method_1” is a method. Here it is declared that this method must be implemented by classes that implement this interface.
The simplest possible full implementation of this interface is:
public class Test1 : GLib.Object, ITest {
public int data_1 { get; set; }
public void method_1() {
}
}
And may be used as follows:
var t = new Test1();
t.method_1();
ITest i = t;
i.method_1();
3.8.1. Defining Prerequisites¶
Interfaces in Vala may not inherit from other interfaces, but they may declare other interfaces to be prerequisites, which works in roughly the same way. For example, it may be desirable to say that any class that implements a List
interface must also implement a Collection
and Traversable
interfaces. The syntax for this is exactly the same as for describing interface implementation in classes:
public interface List : Collection, Traversable {
}
This definition of “List” may not be implemented in a class without “Collection” also being implemented, and so Vala enforces the following style of declaration for a class wishing to implement “List”, where all implemented interfaces must be described:
public class ListClass : GLib.Object, Collection, List {
}
Vala interfaces may also have a class as a prerequisite. If a class name is given in the list of prerequisites, the interface may only be implemented in classes that derive from that prerequisite class. This is often used to ensure that an instance of an interface is also a GLib.Object subclass, and so the interface can be used, for example, as the type of a property.
The fact that interfaces can not inherit from other interfaces is mostly only a technical distinction - in practice Vala’s system works the same as other languages in this area, but with the extra feature of prerequisite classes.
3.8.2. Defining default implementation in methods¶
There’s another important difference between Vala interfaces and Java/C# interfaces: Vala interfaces may have non-abstract methods.
Vala actually allows method implementations in interfaces, then a method with a default implementation must be declared as virtual
. Due to this fact Vala interfaces can act as mixins . This is a restricted form of multiple inheritance.
public interface Callable : GLib.Object {
public abstract bool answering { get; protected set; }
public abstract void answer ();
public virtual bool hang ()
{
answering = false;
return true;
}
}
Interface Callable
defines an abstract property called answering
, where any class implementing this interface can monitor the state of a call, details about answer
a call is a mautter of the implementator, but hang
defines a default implementation to set answering
to false when hanging a call.
public class Phone : GLib.Object, Callable {
public bool answering { get; protected set; }
public void answer ()
{
/* answer code implementation */
}
public static void main ()
{
var f = new Phone ();
if (f.hang ())
stdout.printf("Hand done.\n");
else
stdout.printf("Hand Error!\n");
stdout.printf("END\n");
}
}
When compiling and running, you will find that Phone
class actually no implements Callable.hang()
method, but it is able to use it, then the result is a message Hang done.
public class TechPhone : GLib.Object, Callable
{
public bool answering { get; protected set; }
public void answer ()
{
/* answer code implementation */
}
public bool hang ()
{
answering = false;
stdout.printf ("TechPhone.hang () implementation!");
return false;
}
}
In this case TechPhone
is another implementation to Callable
, then when hang()
method is called on an instance of TechPhone
it will always return false
and print the message TechPhone.hang () implementation!
, hidding completelly Callable.hang()
default implementation.
3.8.3. Properties¶
An interface can define properties that must be implemented for classes. Implementator class must define a property with the same signature and access permissions to the property’s get
and set
.
As any GObject property, you can define a body to property’s set
and get
in the implementator class, when no body is used values are set and get by default. If given, you must define a private
field to store the properties values to be used outside or inside the class.
Callable
interface definition, defines an answering
property. In this case this interface defines a answering
with a protected set
, allowing a read only property for any object using an instance of Callable
, but allows class implementors to write values to it, like TechPhone
class does when implements hang()
method.
3.8.4. Mixins and Multiple Inheritance¶
As described above, Vala while it is backed by C and GObject, can provide a limited multiple inheritance mechanism, by adding virtual methods to Interfaces. Is possible to add some ways to define default method implementations in interface implementor class and allow derived classes to override that methods.
If you define a virtual
method in an interface and implement it in a class, you can’t override interface’s method without leaving derived classes unable to access to interface default one. Consider following code:
public interface Callable : GLib.Object {
public abstract bool answering { get; protected set; }
public abstract void answer ();
public abstract bool hang ();
public static bool default_hang (Callable call)
{
stdout.printf ("At Callable.hang()\n");
call.answering = false;
return true;
}
}
public abstract class Caller : GLib.Object, Callable
{
public bool answering { get; protected set; }
public void answer ()
{
stdout.printf ("At Caller.answer()\n");
answering = true;
hang ();
}
public virtual bool hang () { return Callable.default_hang (this); }
}
public class TechPhone : Caller {
public string number { get; set; }
}
public class Phone : Caller {
public override bool hang () {
stdout.printf ("At Phone.hang()\n");
return false;
}
public static void main ()
{
var f = (Callable) new Phone ();
f.answer ();
if (f.hang ())
stdout.printf("Hand done.\n");
else
stdout.printf("Hand Error!\n");
var t = (Callable) new TechPhone ();
t.answer ();
if (t.hang ())
stdout.printf("Tech Hand done.\n");
else
stdout.printf("Tech Hand Error!\n");
stdout.printf("END\n");
}
}
In this case, we have defined a Callable
interface with a default implementation for abstract bool hang ()
called default_hang
, it could be a static
or virtual
method. Then Caller
is a base class implementing Callable
for the TechPhone
and Phone
classes, while Caller
’s hang ()
method simple call Callable
default implementation. TechPhone
doesn’t do anything and just takes Caller
as base class, using the default method implementations; but Phone
overrides Caller.hang ()
and this makes to use its own implementation, allowing to always call it even if it is cast to Callable
object.
3.8.4.1. Explicit method implementation¶
The explicit interface method implementation allows to implement two interfaces that have methods (not properties) with the same name. Example:
interface Foo {
public abstract int m();
}
interface Bar {
public abstract string m();
}
class Cls: Foo, Bar {
public int Foo.m() {
return 10;
}
public string Bar.m() {
sreturn "bar";
}
}
void main () {
var cls = new Cls ();
message ("%d %s", ((Foo) cls).m(), ((Bar) cls).m());
}
Will output 10 bar.
Note
This feature has been available since Vala version 0.26.