Previous section   Next section

6.3 Polymorphism

There are two powerful aspects to inheritance. One is code reuse. When you create a ListBox class, you're able to reuse some of the logic in the base (Window) class.

What is arguably more powerful, however, is the second aspect of inheritance: polymorphism. Poly means many and morph means form. Thus, polymorphism refers to being able to use many forms of a type without regard to the details.

When the phone company sends your phone a ring signal, it does not know what type of phone is on the other end of the line. You might have an old-fashioned Western Electric phone that energizes a motor to ring a bell, or you might have an electronic phone that plays digital music.

As far as the phone company is concerned, it knows only about the "base type" phone and expects that any "instance" of this type knows how to ring. When the phone company tells your phone to ring, it simply expects the phone to "do the right thing." Thus, the phone company treats your phone polymorphically.

6.3.1 Creating Polymorphic Types

Because a ListBox is-a Window and a Button is-a Window, you expect to be able to use either of these types in situations that call for a Window. For example, a form might want to keep a collection of all the instances of Window it manages so that when the form is opened, it can tell each of its Windows to draw itself. For this operation, the form does not want to know which elements are list boxes and which are buttons; it just wants to tick through its collection and tell each to "draw." In short, the form wants to treat all its Window objects polymorphically.

6.3.2 Creating Polymorphic Methods

To create a method that supports polymorphism, you need only mark it as overridable in its base class. For example, to indicate that the method DrawWindow( ) of class Window in Example 6-1 is polymorphic, simply add the keyword Overridable to its declaration, as follows:

Public Overridable Sub DrawWindow( )

Now each derived class is free to implement its own version of DrawWindow( ) and the method will be invoked polymorphically. To do so, you simply override the base class overridable method by using the keyword Overrides in the derived class method definition, and then add the new code for that overridden method.

Be careful to distinguish the keyword Overridable, which states that a method can be overridden in a derived class, from Overrides, which states that the method is being overridden in the current class. The former says "this method can be overridden if you'd like," the latter says, "Yes, please, I'm overriding the method right now."

In the following excerpt from Example 6-2 (which appears later in this section), ListBox derives from Window and implements its own version of DrawWindow( ):

Public Overrides Sub DrawWindow( )
    MyBase.DrawWindow( ) ' invoke the base method
    Console.WriteLine( _
      "Writing string to the listbox: {0}", listBoxContents)
End Sub 'DrawWindow

The keyword Overrides tells the compiler that this class has intentionally overridden how DrawWindow( ) works. Similarly, you'll override this method in another class, Button, also derived from Window.

In the body of Example 6-2, you'll create three objects: a Window, a ListBox, and a Button. You'll then call DrawWindow( ) on each:

Dim win As New Window(1, 2)
Dim lb As New ListBox(3, 4, "Stand alone list box")
Dim b As New Button(5, 6)
win.DrawWindow( )
lb.DrawWindow( )
b.DrawWindow( )

This works much as you might expect. The correct DrawWindow( ) object is called for each. So far, nothing polymorphic has been done.

The real magic starts when you create an array of Window objects.

Example 6-2 uses an array, which is a collection of objects, all of the same type. You create an array by indicating the type of objects to hold and then allocating space for a given number of those objects. For example, the following code declares winArray to be an array of three Window objects:

Dim winArray(3) As Window

You access the members of the array with parentheses. The first element is accessed with winArray(0), the second with winArray(1), and so forth. Arrays are explained in detail in Chapter 3.

Because a ListBox is-a Window, you are free to place a ListBox into an array of Windows. You can also place a Button into an array of Window objects because a Button is also a Window:

Dim winArray(3) As Window
winArray(0) = New Window(1, 2)
winArray(1) = New ListBox(3, 4, "List box in array")
winArray(2) = New Button(5, 6)

The first line of code declares an array named winArray that will hold three Window objects. The next three lines add new Window objects to the array. The first adds a Window. The second adds a ListBox (which is a Window because ListBox derives from Window), and the third adds a Button (Button also derives from Window).

What happens when you call DrawWindow( ) on each of these objects?

Dim offSet As Integer
For offSet = 0 To 2
    winArray(offSet).DrawWindow( )
Next offSet

This code calls DrawWindow( ) on each element in the array in turn. The value offSet is initialized to zero and is incremented each time through the loop. The value of offSet is used as an index into the array.

All the compiler knows is that it has three Window objects and that you've called DrawWindow( ) on each. If you had not marked DrawWindow( ) as overridable, Window's original DrawWindow( ) method would be called three times.

However, because you did mark DrawWindow( ) as overridable, and because the derived classes override that method, when you call DrawWindow( ) on the array, the right thing happens for each object in the array. Specifically, the compiler determines the runtime type of the actual objects (a Window, a ListBox, and a Button) and calls the right method on each. This is the essence of polymorphism.

The runtime type of an object is the actual (derived) type. At compile time you do not have to decide what kind of objects will be added to your collection, so long as they all derive from the declared type (in this case Window). At runtime the actual type is discovered and the right method is called. This allows you to pick the actual type of objects to add to the collection while the program is running.

The complete code for this example is shown in Example 6-2.

Example 6-2. Virtual methods
Option Strict On
Imports System
Public Class Window

    ' constructor takes two integers to
    ' fix location on the console
    Public Sub New(ByVal top As Integer, ByVal left As Integer)
        Me.top = top
        Me.left = left
    End Sub 'New

    ' simulates drawing the window
    Public Overridable Sub DrawWindow( )
        Console.WriteLine("Window: drawing Window at {0}, {1}", top, left)
    End Sub 'DrawWindow

    ' these members are protected and thus visible
    ' to derived class methods. We'll examine this 
    ' later in the chapter
    Protected top As Integer
    Protected left As Integer

End Class 'Window

' ListBox derives from Window
Public Class ListBox

    Inherits Window

    ' constructor adds a parameter
    Public Sub New(ByVal top As Integer, ByVal left As Integer, ByVal contents As String)
        MyBase.New(top, left) ' call base constructor

        listBoxContents = contents
    End Sub 'New

    ' an overridden version (note keyword) because in the
    ' derived method we change the behavior
    Public Overrides Sub DrawWindow( )
        MyBase.DrawWindow( ) ' invoke the base method
        Console.WriteLine( _
         "Writing string to the listbox: {0}", listBoxContents)
    End Sub 'DrawWindow

    Private listBoxContents As String ' new member variable

End Class 'ListBox

Public Class Button

    Inherits Window

    Public Sub New(ByVal top As Integer, ByVal left As Integer)
        MyBase.New(top, left)
    End Sub 'New

    ' an overridden version (note keyword) because in the
    ' derived method we change the behavior
    Public Overrides Sub DrawWindow( )
        Console.WriteLine( _
          "Drawing a button at {0}, {1}" + ControlChars.Lf, top, Left)
    End Sub 'DrawWindow

End Class 'Button

Public Class Tester

    Shared Sub Main( )
        Dim win As New Window(1, 2)
        Dim lb As New ListBox(3, 4, "Stand alone list box")
        Dim b As New Button(5, 6)

        win.DrawWindow( )
        lb.DrawWindow( )
        b.DrawWindow( )

        Dim winArray(3) As Window
        winArray(0) = New Window(1, 2)
        winArray(1) = New ListBox(3, 4, "List box in array")
        winArray(2) = New Button(5, 6)

        Dim i As Integer
        For i = 0 To 2
            winArray(i).DrawWindow( )
        Next i

    End Sub 'Main

End Class 'Tester

Output:
Window: drawing Window at 1, 2
Window: drawing Window at 3, 4
Writing string to the listbox: Stand alone list box
Drawing a button at 5, 6

Window: drawing Window at 1, 2
Window: drawing Window at 3, 4
Writing string to the listbox: List box in array
Drawing a button at 5, 6

Note that throughout this example, the new overridden methods are marked with the keyword Overrides:

Public Overrides Sub DrawWindow( )

The compiler now knows to use the overridden method when treating these objects polymorphically. The compiler is responsible for tracking the real type of the object and for handling the "late binding" so that it is ListBox.DrawWindow( ) that is called when the Window reference really points to a ListBox object.

6.3.3 Versioning with Overridable and Overrides

In VB.NET, the programmer's decision to override an overridable method is made explicit with the Overrides keyword. This helps you release new versions of your code. Changes to the base class will not break existing code in the derived classes; the requirement to use the Overrides keyword helps prevent that problem.

Here's how: Assume for a moment that the Window base class of the previous example was written by Company A. Suppose also that the ListBox and RadioButton classes were written by programmers from Company B using a purchased copy of the Company A Window class as a base. The programmers in Company B have little or no control over the design of the Window class, including future changes that Company A might choose to make.

Now suppose that one of the programmers for Company B decides to add a Sort( ) method to ListBox:

Public Class ListBox 
   Inherits Window
   Public Overridable Sub Sort( ) 
     '...
   End Sub

This presents no problems until Company A, the author of Window, releases Version 2 of its Window class, and it turns out that the programmers in Company A have also added a Sort( ) method to their public class Window:

Public Class Window
   Public Overridable Sub Sort( ) 
     '...
   End Sub

In other object-oriented languages (such as C++), the new overridable Sort( ) method in Window would now act as a base method for the overridable Sort( ) method in ListBox. The compiler would call the Sort( ) method in ListBox when you intend to call the Sort( ) in Window. In Java, if the Sort( ) in Window had a different return type, the class loader would consider the Sort( ) in ListBox to be an invalid override and would fail to load.

VB.NET prevents this confusion. In VB.NET, an overridable function is always considered to be the root of dispatch; that is, once VB.NET finds an overridable method, it looks no further up the inheritance hierarchy. If a new overridable Sort( ) function is introduced into Window, the runtime behavior of ListBox is unchanged.

When ListBox is compiled again, however, the compiler generates a warning:

Module1.vb(31) : warning BC40005: sub 'Sort' shadows an 
overridable method in a base class. To override the 
base method, this method must be declared 'Overrides'.

To remove the warning, the programmer must indicate what he intends. He can mark the ListBox Sort( ) method Shadows, to indicate that it is not an override of the Overridable method in Window:

Public Class ListBox
    Inherits Window

    Public Shadows Sub Sort( )
        '...
    End Sub 'Sort

This action removes the warning. If, on the other hand, the programmer does want to override the method in Window, he need only use the Overrides keyword to make that intention explicit:

Public Class ListBox
    Inherits Window

    Public Overrides Sub Sort( )
        '...
    End Sub
 'Sort

  Previous section   Next section
Top