A recent comment on my post about overriding prompted me to write this second post about interfaces and why you might use them. Its clear to me that I havent done a very good job of explaining why you should or would use them.
In some languages you can create classes that inherit from one other and one other only. Xojo is this way. Some others let you inherit from several superclasses but this has a set of issues that Xojo avoids by limiting things to single inheritance. Java is also this way and does not support multiple inheritance.
So what do you do in the case you have a class you’ve created and you want it to “inherit” from multiple other classes ? Suppose you have a class that needs to inherit from “InputStream” so it can be read from AND also needs to inherit from “OutputStream” so it can be written to ? Xojo has such a class – BinaryStream.
The way to achieve this is actually NOT to try & inherit from two super classes but to make the InputStream and OutputStream into interfaces and then have your class implement both interfaces. In fact this IS what BinaryStream does – it implements both the Readable and Writeable interfaces so you can read from it and write to it.
You implement the methods on each interface and then any other methods on your code that takes a Readable parameter you can pass a BinaryStream to, since BinaryStream IsA Readable. And any of your code that takes a Writeable parameter you can also pass the BinaryStream to since it IsA Writeable.
As well you can extend interfaces just like any other class.
So its perfectly legal to write a method like
Sub DoREadableStuff(extends instance as Readable)
just as you would for any other type in Xojo.
This gives you a lot of flexibility in how you use & implement interfaces.
About the only thing you might run into is what to do if you have two interfaces that you want a class to implement and they have methods that you have to implement that are named the same ?
As long as the full signature of the methods, method name and all parameters, are unique this isnt a problem as you can implement both and you will just appear to have 2 or more overloads of the same method just taking different parameters.
But what if they have the same parameters ?
If you just add both interfaces then you will have to methods with the same name and the same parameters and Xojo will not compile your program any more because its ambiguous which one would be used when.
And this is where a little used aspect of Xojo comes into use.
Lets set up a small example so you can follow along as that will make things much clearer.
Start a new desktop project.
To the project add
- a class named class1
- a class interface named interface1
- add a method to interface1 named Connect
- a second class interface named interface2
- add a method to interface2 also named Connect
Now we’re going to make it so class1 implements both interface1 and interface2. Click on Class1 and make sure the Inspector is showing. Click on the Interfaces button in the Inspector. Select both Interface1 and Interface2. I often also select the “insert #pragma error” in each method option at the bottom of the pane.
Click OK and empty stubs for all the methods in Interface1 and Interface2 will be added to Class1. And note we do have 2 connect methods.
If you try to run or analyze the project you will get a “duplicate method definition” error – this is exactly as expected since we have two identical signatures (method name and parameters)
And now we’ll fix this. What we need to tell Xojo is that “this method implements the Connect method from interface1 and this other method implements the Connect method from Interface2”.
Right click on the first Connect method. And select the “Show Implements” option from the contextual menu. Notice that when you do this an extra field shows up in the Inspector.
This extra field is used for exactly what we just stated – telling Xojo which Connect method each of the Connect overloads actually implement. So for the first one we’ll put in the field Interface1.Connect. Then click the second Connect method and in the field put Interface2.Connect
You’d think we’re done at this point because those two method now say which method from which interface they implement. However, they are still exact duplicates and the compiler will still complain about that. So rename the first one Interface1Connect and the second Interface2Connect. I’ve renamed this way JUST for clarity for this example. You could rename them anything you liked that doesnt cause other errors.
And now for the proof that this works.
In our sample applications Window1.Open event (or opening for those using 2019r2 and newer) lets put in the following code
Dim i As interface1 = New Class1 break i.Connect Dim j As interface2 = Interface2(i) break j.Connect
Recall that a Class1 IsA Interface1 because it implements that interface. And that it is also an Interface2 because it also implements that interface. If you run this code now you wont get any errors BUT it is hard to see that it is doing exactly what we’ve told it to.
If you run & step INTO each call to Connect you will see that because in the first case i IsA Interface1 that stepping into i.Connect does indeed call Interface1Connect – the exact method we said implemented the Interface1.Connect method.
And when you get to the second break statement and step into j.Connect you will see that, because j IsA Interface2 that this call to Connect will end up in Class1’s Interface2Connect method – the one set said implemented Interface2.Connect.