3.9.4 Interface Types
[An interface type is an abstract tagged type that
provides a restricted form of multiple inheritance. A tagged type, task
type, or protected type may have one or more interface types as ancestors.]
Glossary entry: An
interface type is a form of abstract tagged type which has no components
or concrete operations except possibly null procedures. Interface types
are used for composing other interfaces and tagged types and thereby
provide multiple inheritance. Only an interface type can be used as a
progenitor of another type.
Language Design Principles
The rules are designed so that an interface can
be used as either a parent type or a progenitor type without changing
the meaning. That's important so that the order that interfaces are specified
in a derived_type_definition
is not significant. In particular, we want:
type Con1 is new Int1 and Int2 with null record;
type Con2 is new Int2 and Int1 with null record;
mean exactly the same thing.
An interface with the reserved word limited,
task, protected, or synchronized in its definition
is termed, respectively, a limited interface, a task interface,
a protected interface, or a synchronized interface. In
task and protected interfaces are synchronized interfaces, and all synchronized
interfaces are limited interfaces.
Glossary entry: A
synchronized entity is one that will work safely with multiple tasks
at one time. A synchronized interface can be an ancestor of a task or
a protected type. Such a task or protected type is called a synchronized
task or protected type derived from an interface is a tagged type.] Such
a tagged type is called a synchronized tagged type, as are synchronized
interfaces and private extensions whose declaration includes the reserved
Proof: The full
definition of tagged types given in 3.9 includes
task and protected types derived from interfaces.
class-wide type associated with a tagged task type (including a task
interface type) is a task type, because “task” is one of
the language-defined classes of types (see 3.2).
However, the class-wide type associated with an interface is not
an interface type, as “interface” is not one of the
language-defined classes (as it is not closed under derivation). In this
sense, “interface” is similar to “abstract”.
The class-wide type associated with an interface is a concrete (nonabstract)
indefinite tagged composite type.
includes generic formal private extensions, as explained in 12.5.1.
A task interface is an [abstract] task type. A
protected interface is an [abstract] protected type.
Proof: The “abstract”
follows from the definition of an interface type.
Reason: This ensures
that task operations (like abort and the Terminated attribute) can be
applied to a task interface type and the associated class-wide type.
While there are no protected type operations, we apply the same rule
to protected interfaces for consistency.
Proof: This follows
from the syntax and the fact that discriminants are not allowed for interface
in an interface_list
names a progenitor subtype; its type is the progenitor type.
An interface type inherits user-defined primitive subprograms from each
progenitor type in the same way that a derived type inherits user-defined
primitive subprograms from its progenitor types (see 3.4).
Glossary entry: A
progenitor of a derived type is one of the types given in the definition
of the derived type other than the first. A progenitor is always an interface
type. Interfaces, tasks, and protected types may also have progenitors.
All user-defined primitive subprograms of an interface
type shall be abstract subprograms or null procedures.
An interface derived from a task interface shall
include the reserved word task in its definition; any other type
derived from a task interface shall be a private extension or a task
type declared by a task declaration (see 9.1).
An interface derived from a protected interface
shall include the reserved word protected in its definition; any
other type derived from a protected interface shall be a private extension
or a protected type declared by a protected declaration (see 9.4).
An interface derived from a synchronized interface
shall include one of the reserved words task, protected,
or synchronized in its definition; any other type derived from
a synchronized interface shall be a private extension, a task type declared
by a task declaration, or a protected type declared by a protected declaration.
Reason: We require
that an interface descendant of a task, protected, or synchronized interface
repeat the explicit kind of interface it will be, rather than simply
inheriting it, so that a reader is always aware of whether the interface
provides synchronization and whether it may be implemented only by a
task or protected type. The only place where inheritance of the kind
of interface might be useful would be in a generic if you didn't know
the kind of the actual interface. However, the value of that is low because
you cannot implement an interface properly if you don't know whether
it is a task, protected, or synchronized interface. Hence, we require
the kind of the actual interface to match the kind of the formal interface
No type shall be derived from both a task interface
and a protected interface.
Reason: This prevents
a single private extension from inheriting from both a task and a protected
interface. For a private type, there can be no legal completion. For
a generic formal derived type, there can be no possible matching type
(so no instantiation could be legal). This rule provides early detection
of the errors.
In addition to the places where Legality Rules
normally apply (see 12.3), these rules apply
also in the private part of an instance of a generic unit.
This paragraph is intended to apply to all of the
Legality Rules in this subclause clause.
We cannot allow interface types which do not obey these rules, anywhere.
Luckily, deriving from a formal type (which might be an interface) is
not allowed for any tagged types in a generic body. So checking in the
private part of a generic covers all of the cases.
Nonlimited interface types have predefined nonabstract
equality operators. These may be overridden with user-defined abstract
equality operators. Such operators will then require an explicit overriding
for any nonabstract descendant of the interface.
Example of a limited interface and a synchronized
interface extending it:
type Queue is limited interface;
procedure Append(Q : in out Queue; Person : in Person_Name) is abstract;
procedure Remove_First(Q : in out Queue;
Person : out Person_Name) is abstract;
function Cur_Count(Q : in Queue) return Natural is abstract;
function Max_Count(Q : in Queue) return Natural is abstract;
-- See 3.10.1 for Person_Name.
Queue_Error : exception;
-- Append raises Queue_Error if Cur_Count Count(Q) = Max_Count(Q)
-- Remove_First raises Queue_Error if Cur_Count Count(Q) = 0
type Synchronized_Queue is synchronized interface and Queue; -- see 9.11
procedure Append_Wait(Q : in out Synchronized_Queue;
Person : in Person_Name) is abstract;
procedure Remove_First_Wait(Q : in out Synchronized_Queue;
Person : out Person_Name) is abstract;
procedure Transfer(From : in out Queue'Class;
To : in out Queue'Class;
Number : in Natural := 1) is
Person : Person_Name;
for I in 1..Number loop
This defines a Queue interface
defining a queue of people. (A similar design could be created to define
any kind of queue simply by replacing Person_Name by an appropriate type.)
The Queue interface has four dispatching operations, Append, Remove_First,
Cur_Count, and Max_Count. The body of a class-wide operation, Transfer
is also shown. Every nonabstract extension of Queue must provide implementations
for at least its four dispatching operations, as they are abstract. Any
object of a type derived from Queue may be passed to Transfer as either
the From or the To operand. The two operands need not be of the same
type in any given call.
The Synchronized_Queue interface
inherits the four dispatching operations from Queue and adds two additional
dispatching operations, which wait if necessary rather than raising the
Queue_Error exception. This synchronized interface may only be implemented
by a task or protected type, and as such ensures safe concurrent access.
type Fast_Food_Queue is new Queue with record ...;
procedure Append(Q : in out Fast_Food_Queue; Person : in Person_Name);
procedure Remove_First(Q : in out Fast_Food_Queue; Person : out in Person_Name);
function Cur_Count(Q : in Fast_Food_Queue) return Natural;
function Max_Count(Q : in Fast_Food_Queue) return Natural;
Cashier, Counter : Fast_Food_Queue;
-- Add George (see 3.10.1) to the cashier's queue:
Append (Cashier, George);
-- After payment, move George to the sandwich counter queue:
Transfer (Cashier, Counter);
An interface such as Queue
can be used directly as the parent of a new type (as shown here), or
can be used as a progenitor when a type is derived. In either case, the
primitive operations of the interface are inherited. For Queue, the implementation
of the four inherited routines must be provided. Inside the call of Transfer,
calls will dispatch to the implementations of Append and Remove_First
for type Fast_Food_Queue.
type Serial_Device is task interface; -- see 9.1
procedure Read (Dev : in Serial_Device; C : out Character) is abstract;
procedure Write(Dev : in Serial_Device; C : in Character) is abstract;
The Serial_Device interface
has two dispatching operations which are intended to be implemented by
task entries (see 9.1).
Extensions to Ada 95
Interface types are new. They
provide multiple inheritance of interfaces, similar to the facility provided
in Java and other recent language designs.
Wording Changes from Ada 2005
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe