Articles Class Implementing Two Interfaces with Duplicate Method Names by Jim McKeeth

emailx45

Бывалый
Staff member
Moderator
Class Implementing Two Interfaces with Duplicate Method Names
[SHOWTOGROUPS=4,20]
Jim McKeeth
26/11/2019

Delphi only supports single inheritance. A Delphi class can only descend from a single parent class, but a Delphi class can implement multiple interfaces.

Code:
type
TAthlete = class(THuman, IWalker, IJumper)
The TAthlete descends from the THuman parent class (which presumably descends from TInterfacedObject) and it implements both the IWalker and IJumper interfaces. What if both IWalker and IJumper contain a run method.
Code:
type
THuman = class(TInterfacedObject)
procedure walk; virtual;
end;

IJumper = Interface(IInterface)
procedure run;
end;

IWalker = Interface(IInterface)
procedure run;
end;

TAthlete = class(THuman, IWalker, IJumper)
end;

Data Model

Right now TAthlete doesn't implement the members of IWalker or IJumper.
  • [dcc32 Error] E2291 Missing implementation of interface method IJumper.run
  • [dcc32 Error] E2291 Missing implementation of interface method IWalker.run
When we implement these interfaces in TAthlete, what if we want to have a different run method for IWalker vs IJumper? Enter the Method Resolution Clause.
Interface Method Resolution Clause
When a class implements two or more interfaces that have identically named methods, use method resolution clauses to resolve the naming conflicts. You can override the default name-based mappings by including method resolution clauses in a class declaration. We might implement those interfaces like this:
Code:
type
TAthlete = class(THuman, IWalker, IJumper)
public
procedure IWalker.run = PowerWalk;
procedure IJumper.run = RealRun;
private
procedure PowerWalk;
procedure RealRun;
end;

But what happens if I call Run on a class reference to an TAthlete object? It doesn't exist. There is no Run method on TAthlete, and both PowerWalk and RealRun are private, so they aren't accessible via a class reference either.

Code:
  var Athlete := TAthlete.Create;
try
(* These give E2003 Undeclared identifier
Athlete.run; // There is no Run method on TAthlete
Athlete.PowerWalk; // PowerWalk is Private
Athlete.RealRun; // Also private *)

// To access Run we must have an Interface reference
IWalker(Athlete).Run; // Calls TAthlete's RealRun method
IJumper(Athlete).Run; // Calls TAthlete's PowerWalk method
finally
Athlete.Free;
end;

If we wanted to call Run on TAthlete we could do that with a little change.
Code:
type
TAthlete = class(THuman, IWalker, IJumper)
public
procedure IWalker.Run = PowerWalk;
procedure Run;
private
procedure PowerWalk;
end;

Now IJumper uses the default name-based mapping, which IWalker uses the manually mapped method
Code:
  var Athlete := TAthlete.Create;
try
Athlete.Run; // We now have a Run method
IWalker(Athlete).Run; // Calls TAthlete's PowerWalk method (not a real run)
IJumper(Athlete).Run; // Calls the real Run method on TAthlete
finally
Athlete.Free;
end;
It seems like it would usually be a good idea to be explicit in all the methods implemented by interfaces when you have a conflict like this, but there could be a reason to be less explicit in certain use cases. It is great that Delphi gives you the flexibility to implement this either way necessary.

[/SHOWTOGROUPS]
 
Last edited:
Top