Delphi only supports single inheritance. A Delphi class can only descend from a single parent class, but a Delphi class can implement multiple interfaces.
1 2 |
<span style="color:#000084;font-weight:bold;">type</span> TAthlete = <span style="color:#000084;font-weight:bold;">class</span>(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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span style="color:#000084;font-weight:bold;">type</span> THuman = <span style="color:#000084;font-weight:bold;">class</span>(TInterfacedObject) <span style="color:#000084;font-weight:bold;">procedure</span> walk; <span style="color:#000084;font-weight:bold;">virtual</span>; <span style="color:#000084;font-weight:bold;">end</span>; IJumper <span style="color:#000084;font-weight:bold;">= Interface</span>(IInterface) <span style="color:#000084;font-weight:bold;">procedure</span> run; <span style="color:#000084;font-weight:bold;">end</span>; IWalker <span style="color:#000084;font-weight:bold;">= Interface</span>(IInterface) <span style="color:#000084;font-weight:bold;">procedure</span> run; <span style="color:#000084;font-weight:bold;">end</span>; TAthlete = <span style="color:#000084;font-weight:bold;">class</span>(THuman, IWalker, IJumper) <span style="color:#000084;font-weight:bold;">end</span>; |
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:
1 2 3 4 5 6 7 8 9 |
<span style="color:#000084;font-weight:bold;">type</span> TAthlete = <span style="color:#000084;font-weight:bold;">class</span>(THuman, IWalker, IJumper) <span style="color:#000084;font-weight:bold;">public</span> <span style="color:#000084;font-weight:bold;">procedure</span> IWalker.run = PowerWalk; <span style="color:#000084;font-weight:bold;">procedure</span> IJumper.run = RealRun; <span style="color:#000084;font-weight:bold;">private</span> <span style="color:#000084;font-weight:bold;">procedure</span> PowerWalk; <span style="color:#000084;font-weight:bold;">procedure</span> RealRun; <span style="color:#000084;font-weight:bold;">end</span>;<br><img alt=" " height="424" src="https://blogs.embarcadero.com/wp-content/uploads/2020/08/5226.method_5F00_resolution.png" width="549" style="cursor: zoom-in;"> |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<span style="color:#000084;font-weight:bold;">var</span> Athlete := TAthlete.Create; <span style="color:#000084;font-weight:bold;">try</span> <span style="color:#808080;">(* These give E2003 Undeclared identifier</span> <span style="color:#808080;"> Athlete.run; // There is no Run method on TAthlete</span> <span style="color:#808080;"> Athlete.PowerWalk; // PowerWalk is Private</span> <span style="color:#808080;"> Athlete.RealRun; // Also private *)</span> <span style="color:#808080;">// To access Run we must have an Interface reference</span> IWalker(Athlete).Run; <span style="color:#808080;">// Calls TAthlete's RealRun method</span> IJumper(Athlete).Run; <span style="color:#808080;">// Calls TAthlete's PowerWalk method</span> <span style="color:#000084;font-weight:bold;">finally</span> Athlete.Free; <span style="color:#000084;font-weight:bold;">end</span>;<br><br> |
If we wanted to call Run on TAthlete we could do that with a little change.
1 2 3 4 5 6 7 8 |
<span style="color:#000084;font-weight:bold;">type</span> TAthlete = <span style="color:#000084;font-weight:bold;">class</span>(THuman, IWalker, IJumper) <span style="color:#000084;font-weight:bold;">public</span> <span style="color:#000084;font-weight:bold;">procedure</span> IWalker.Run = PowerWalk; <span style="color:#000084;font-weight:bold;">procedure</span> Run; <span style="color:#000084;font-weight:bold;">private</span> <span style="color:#000084;font-weight:bold;">procedure</span> PowerWalk; <span style="color:#000084;font-weight:bold;">end</span>; |
Now IJumper uses the default name-based mapping, which IWalker uses the manually mapped method
1 2 3 4 5 6 7 8 |
<span style="color:#000084;font-weight:bold;">var</span> Athlete := TAthlete.Create; <span style="color:#000084;font-weight:bold;">try</span> Athlete.Run; <span style="color:#808080;">// We now have a Run method</span> IWalker(Athlete).Run; <span style="color:#808080;">// Calls TAthlete's PowerWalk method (not a real run)</span> IJumper(Athlete).Run; <span style="color:#808080;">// Calls the real Run method on TAthlete</span> <span style="color:#000084;font-weight:bold;">finally</span> Athlete.Free; <span style="color:#000084;font-weight:bold;">end</span>; |
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.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition