Sometimes when you create a subclass you want to your subclass to do something different than the class it inherits from. But you want to retain the API or reuse the method name because its perfectly suited.
When you do this and add a method that has the EXACT SAME signature as the one in the superclass you are overriding the method. The signature includes the method name, parameters and their types but excludes the return type.
This can be very useful if you have a class that has many specialized subclasses. For instance, the framework has several subclasses of TCP Sockets; HTTPSockets, SMTPSockets and POP3Sockets. What an HTTP Socket does when you call it Connect method may be different than what the SMTP Socket does when you call its Connect method and also different from what the POP3 Socket does when you call its connect method.
Suppose you have classes like :
Class BaseClass
Sub Foo()
System.debuglog CurrentMethodName
End Sub
End Class
Class SubClassA
Inherits BaseClass
Sub Foo()
System.debuglog CurrentMethodName
End Sub
End Class
Class SubclassB
Inherits BaseClass
Sub Foo()
System.debuglog CurrentMethodName
End Sub
End Class
And code that uses these classes as
Dim c As BaseClass
c = New SubClassA
c.foo
c = New SubclassB
c.foo
What you will see in the debuglog is something like this on macOS.
10:09:20 PM : My Application Launched
SubClassA.Foo
SubclassB.Foo
10:09:27 PM : My Application Ended
Whats going on ?
Methods are, unlike properties, virtual. What that means is that the correct method to call will be determined NOT at compile time, but at runtime and based on the actual instance and its method list. This is really handy in lots of ways as I already noted.
You can, as we did in our code, declare the type as the most generic – like we did – since all subclasses are instance of the subclass AND whatever the superclasses that its inherited.
In our code above an instance of SubClassA is both a SubclassA AND a BaseClass.
There are some glitches that you can still run into. For instance this bug report notes one spot where the correct method to call is NOT based on the runtime type but the compile time type (which is really counter intuitive)
That said, overriding is very useful in a class hierarchy.
Have fun with this.
Thanks for the blog posts – I read them with interest 😉
One question: interfaces seem to do the same thing, so when should you use which?
Overriding occurs within a single class hierarchy. Subclasses can override methods in their super classes to alter how they behave.
Interfaces though are a slightly different, but somewhat related, item. An interface is useful when you have classes that are NOT related by inheritance but you want them to expose a similar API – and that API is defined in the interface. The interface lists all the methods that any implementor MUST have.
The Xojo framework has a few instances where interfaces are used. Readable and Writeable are two such cases. The Readable interface is implemented by BinaryStream, IPCSocket, Serial, Stdin, TCPSocket and TextInputStream. The stream classes, BinaryStream and TextInputStream could be subclasses of a “Stream” class, but they most definitely do not have any inherited relationship to Sockets.
But since all of these classes are “Readable” they all implement the Readable interface and could be passed as a parameter to any method that took a “Readable” object as a parameter like
Sub ReadStuff(readableObject as Readable)
Note that a class that implements an interface will, when tested using ISA, return TRUE. For instance, you would hit the break point in the following code :
dim bs as BinaryStream
if bs ISA Readable then
break
end if
One further thing about interfaces – any class MAY implement one or more interfaces.
BinaryStream implements both Readable and Writeable.
Thanks for the clarification 👍