Previous section   Next section

8.7 Overriding Interface Implementations

An implementing class is free to mark any or all of the methods that implement the interface as overridable. Derived classes can then override or provide new implementations. For example, a Document class might implement the IStorable interface and mark the Read() and Write( ) methods as overridable. The Document might Read( ) and Write( ) its contents to a File type. The developer might later derive new types from Document, such as perhaps a Note or EmailMessage type. While the Document class implements Read( ) and Write to save to a File, the Note class might implement Read( ) and Write( ) to read from and write to a database.

Example 8-5 strips down the complexity of the previous examples and illustrates overriding an interface implementation. In this example, you'll derive a new class named Note from the Document class.

Document implements the IStorable-required Read( ) method as an overridable method, and Note overrides that implementation.

Notice that Document does not mark Write( ) as overridable. You'll see the implications of this decision in the analysis section that follows the output.

The complete listing is shown in Example 8-5 and analyzed in detail following.

Example 8-5. Overriding an interface implementation
Option Strict On

Imports Microsoft.VisualBasic
Imports System

Namespace OverridingInterfaces

    Interface IStorable
        Sub Read( )
        Sub Write( )
    End Interface

    ' simplify Document to implement only IStorable
    Public Class Document : Implements IStorable

        ' the document constructor
        Public Sub New(ByVal s As String)
            Console.WriteLine("Creating document with: {0}", s)
        End Sub

        ' make read virtual
        Public Overridable Sub Read( ) Implements IStorable.Read
            Console.WriteLine("Document Virtual Read Method for IStorable")
        End Sub

        ' NB: Not virtual!
        Public Sub Write( ) Implements IStorable.Write
            Console.WriteLine("Document Write Method for IStorable")
        End Sub

    End Class

    ' derive from Document
    Public Class Note : Inherits Document

        Public Sub New(ByVal s As String)
            MyBase.New(s)
            Console.WriteLine("Creating note with: {0}", s)
        End Sub

        ' override the Read method
        Public Overrides Sub Read( )
            Console.WriteLine("Overriding the Read method for Note!")
        End Sub

        ' implement my own Write method
        Public Shadows Sub Write( )
            Console.WriteLine("Implementing the Write method for Note!")
        End Sub

    End Class

    Class Tester

        Public Sub Run( )
            ' create a Document object
            Dim theNote As Document = New Note("Test Note")

            ' cast the Document to IStorable
            If TypeOf theNote Is IStorable Then
                Dim isNote As IStorable = theNote
                isNote.Read( )
                isNote.Write( )
            End If

            Console.WriteLine(vbCrLf)

            ' direct call to the methods
            theNote.Read( )
            theNote.Write( )

            Console.WriteLine(vbCrLf)

            ' create a note object
            Dim note2 As New Note("Second Test")

            ' Cast the note to IStorable
            If TypeOf note2 Is IStorable Then
                Dim isNote2 As IStorable = note2
                isNote2.Read( )
                isNote2.Write( )
            End If
            Console.WriteLine(vbCrLf)

            ' directly call the methods
            note2.Read( )
            note2.Write( )
        End Sub

        Public Shared Sub Main( )
            Dim t As New Tester( )
            t.Run( )
        End Sub

    End Class

End Namespace


Output:
Creating document with: Test Note
Creating note with: Test Note
Overriding the Read method for Note!
Document Write Method for IStorable

Overriding the Read method for Note!
Document Write Method for IStorable

Creating document with: Second Test
Creating note with: Second Test
Overriding the Read method for Note!
Document Write Method for IStorable

Overriding the Read method for Note!
Implementing the Write method for Note!

In Example 8-5, the IStorable interface is simplified for clarity's sake:

Interface IStorable
    Sub Read( )
    Sub Write( )
End Interface

The Document class implements the IStorable interface:

Public Class Document : Implements IStorable

The designer of Document has opted to make the Read( ) method overridable but not to make the Write( ) method overridable:

Public Overridable Sub Read( ) Implements IStorable.Read
Public Sub Write( ) Implements IStorable.Write

In a real-world application, you would almost certainly mark both as overridable, but I've differentiated them to demonstrate that the developer is free to pick and choose which methods can be overridden.

The new class, Note, derives from Document:

Public Class Note : Inherits Document

It is not necessary for Note to override Read( ) (it may shadow it instead), but it is free to do so and has done so here:

Public Overrides Sub Read( )

To illustrate the implications of marking an implementing method as overridable, the Run( ) method calls the Read( ) and Write( ) methods in four ways:

As you'll see, the base class reference and the derived class reference act just as they always have: overridable methods are implemented polymorphically and non-overridable methods are not. The interfaces created from these references work just like the references themselves: overridable implementations of the interface methods are polymorphic, and non-overridable methods are not.

The one surprising aspect is this: when you call the non-polymorphic Write( ) method on the IStorable interface cast from the derived Note, you actually get the Document's Write( ) method. This is because Write( ) is implemented in the base class and is not overridable.

To accomplish the first two calls, a Document (base class) reference is created, and the address of a new Note (derived) object created on the heap is assigned to the Document reference:

Dim theNote As Document = New Note("Test Note")

An interface reference is created (isNote) and theNote is cast to the IStorable interface:

If TypeOf theNote Is IStorable Then
    Dim isNote As IStorable = theNote

You then invoke the Read( ) and Write( ) methods through that interface. The output reveals that the Read( ) method is responded to polymorphically and the Write( ) method is not, just as you would expect:

Overriding the Read method for Note!
Document Write Method for IStorable

The Read( ) and Write( ) methods are then called directly on the derived object itself:

theNote.Read( )
theNote.Write( )

and once again you see the polymorphic implementation has worked:

Overriding the Read method for Note!
Document Write Method for IStorable

In both cases, the Read( ) method of Note was called, but the Write( ) method of Document was called.

To prove to yourself that this is a result of the overriding method, you next create a second Note object, this time assigning its address to a reference to a Note. This will be used to illustrate the final cases (i.e., a call through a derived object and a call through an interface created from the derived object):

Dim note2 As New Note("Second Test")

Once again, when you cast to a reference, the overridden Read( ) method is called. When, however, methods are called directly on the Note object:

note2.Read( )
note2.Write( )

the output reflects that you've called a Note and not an overridden Document:

Overriding the Read method for Note!
Implementing the Write method for Note!

  Previous section   Next section
Top