Interfaces (Was: Multiple inheritance)

Fri, 30 May 1997 00:35:56 +0200



Peter Gerwinski wrote:

> What are these "interfaces"?  (Somebody already explained it to me some
> time ago, but I have forgotten ... #-)

Ok, here are some explanations from "Java in a Nutshell", with some comments
by me, and some ideas how to implement them in gpc:

"[Text describes a situation that seems to need MI]
\footnote{ C++ allows classes to have more than one superclass, using a
technique known as 'multiple inheritance'. Multiple inheritance opens up
a can of worms, so Java replaces it with what many believe is a more elegant
solution. }

Java's solution to this problem is called an interface. An interface looks
a lot like an abstract class, except that it uses the keyword 'interface'
instead of the words 'abstract' and 'class'. [...]

While an 'abstract' class may define some 'abstract' methods and some non-
'abstract' methods, all the methods defined within an interface are
implicitly 'abstract'. [The 'abstract' keyword can, but doesn't have to, be
omitted.] [Note: In Java, all methods are what we call virtual.]
A further restriction on interfaces is that any variables declared in an
interface must be 'static' and 'final' - that is, they must be constants
[ordinary constants, not initialized variables, nor object constants -
since ordinary constants don't appear in BP's object type declarations at
all, this restriction effectively means there can't be any variables or
constants in an interface in gpc, so an interface consists only of
declarations of abstract virtual methods (**, see below)].

So what can we do with an interface? Just as a class 'extends' [is derived
from] its superclass, it also optionally 'implements' an interface.
'implements' s a Java keyword that can appear in a class declaration
following the 'extends' clause. 'implements' should be followed by the name
of the interface that the class implements. In order to implement an
interface, a class must first delcare the interface in an 'implements'
clause, and then it must provide an implementation (i.e. body) for all of
the 'abstract' methods of the interface.
\footnote{ This is the real difference between multiple inheritance in C++
and interfaces in Java. In C++, a class can interit method implementations
from more than one superclass. In Java, a class can inherit actual
implementations only from one superclass. It can inherit additional
'abstract' methods [declarations] from interfaces, but it must provide its
own implementation of those methods. It is rare, however, to actually be able
to use C++ multiple inheritance to inherit useful, non-trivial
implementations from more than one class. The elegance and simplicity of
Java's interface more than compensate for the inability to inherit
implementations from more than one class. }
[...]

Using Interfaces

[...] [Syntax translated to Pascal, tentatively:]
Type
  Drawable=Interface [this wouldn't introduce a new reserved identifer, but
                      I'm not sure if it can be parsed easily.]
    Procedure Draw(dw:DrawWindow); ['virtual;' or 'abstract;' optionally]
    [possibly other methods]
  End;

  [declaration of type Rectangle that doesn't have a Draw method]

  DrawableRectangle=Object(Rectangle;Drawable)
    [in Java this is:
     public class DrawableRectangle extends Rectangle implements Drawable]
    x,y:Double;
    Constructor Init(w,h:Double);
    Procedure Draw(dw:DrawWindow); virtual;
    [other methods, destructor]
  End;

[implementation of all methods, constructor and destructor]

Var
  d:^Drawable;
  r:^Rectangle;
  dr:^DrawableRectangle;

Begin
  New(dr,Init(2.3,4.5));
  d:=dr;
  r:=dr;
  dr.Draw;
  d.Draw;
End.
[...]

[Classes can implement several interfaces ('implements Drawable, Scalable').]
[...]

Extending Interfaces
[...]
public interface Transformable extends Scalable, Rotateable, Reflectable
public interface DrawingObject extends Drawbable, Transformable
public class Shape implements DrawingObject
[So there is 'multiple inheritance' between interfaces - which makes no
problems!]

[...]
No 'implements' clause is permitted in an interface declaration.

An interface body may not contain any constructors, static class
initializers, instance variables, or class methods. [...]"

Concerning a possible implementation in gpc:

AFAICS, the only thing that really makes problems are variables (or
parameters) of interface types.

First of all, since interfaces can't be instantiated, such variables or
parameters must be pointers (or var parameters). In Java, this is implied,
since *all* object variables are pointers.

But such a pointer must point to the actual object and to the methods
declared in the interface. Since different object types can implement the
same interface, those methods will not always be at the same VMT offset.
However, it is possible to guarantee that all methods of one interface
are at consecutive VMT offsets (in the order they're declared in the
interface). (*, see below)

So one needs the VMT offset of the interface's first method.

I can see (at least) two solutions:

- A "pointer" to an interface variable consists of two parts: the actual
  pointer to the variable, and the VMT offset of the first method (or,
  alternatively, directly the adress of the first method in the VMT).

  Disadvantage: The pointer gets twice as big. The difference must be
  considered when assigning it to another pointer (this could be an untyped
  pointer or a pointer of one of the "parent" interfaces - in the latter
  case the VMT offset has to be adjusted).

- The VMT must contain information about all interfaces that are implemented
  together with the addresses of their methods. However, since different
  object types can implement different interfaces, and one type can
  implement more than one interface, I can't think of a method that doesn't
  involve some kind of searching (searching the wanted interface out of
  possibly many interfaces).

So this is basically a speed vs. size tradeoff. For most applications,
I think, speed is to be favoured (and 32 bit programming favours speed,
anyway), so I'd prefer the first solution. I can't see a solution without
a need of either searching or additional data *per variable* (whereas
additional data per VMT wouldn't matter so much).

(*)
If we allow the following inheritance between interfaces
  A
 / \
B   C
 \ /
  D
(and I don't see a reason why not), the methods of A will have to be
duplicated in D, so that it's possible to "cast" a ^D into a ^B or a ^C.
So the "VMT" of D would look like

Methods of A     \_ "B"
New methods of B /
Methods of A     \_ "C"
New methods of C /

The two parts "Methods of A" would contain the same information, but since
this is only in the VMTs, I don't think it wastes very much memory.

I hope I was clear in most things. If not, please ask again. Of course,
there will be some "details" left to be solved, but I hope, no major
problems.

(**)
This last thing is a bit off-topic to interfaces, so I put it at the end.
Thinking about object constants again, I think there can be "un-virtual"
constants, i.e. constants that can be redeclared in child classes, but the
parent class will always use their own constants. So, effectively, they're
oridinary constants, with the only difference that they're visibility is
limited to the object type (applying the usual rules of
public/protected/private). These constants don't have to be typed, of course.
Also, one can imagine "un-virtual" class variables, very similarly.
They should be relatively easy to implement (since the only difference to
ordinary constants/variables is the visibility). If this is done, interfaces
could contain constants, like in Java.
-- 
Frank Heckenbach, Erlangen, Germany
heckenb@mi.uni-erlangen.de
Turbo Pascal:   http://www.mi.uni-erlangen.de/~heckenb/programs.htm
Internet links: http://www.mi.uni-erlangen.de/~heckenb/links.htm


Frank Heckenbach (heckenb@mi.uni-erlangen.de)

HTML conversion by Lluís de Yzaguirre i Maura
Institut de Lingüística Aplicada - Universitat "Pompeu Fabra"
e-mail: de_yza@upf.es