25.6. Generic Classes The concept of a data structure (e.g., a stack) that contains data elements can be understood independently of the element type it manipulates. A generic class provides a means for describing a class's capabilities in a type-independent manner. You can then instantiate type-specific objects of the generic class. This capability is an opportunity for software reusability. Once you have a generic class, you can use a simple, concise notation to indicate the actual type(s) that should be used in place of the class's type parameter(s). At compilation time, the compiler ensures the type safety of your code, and the runtime system replaces type parameters with actual arguments to enable your client code to interact with the generic class. One generic Stack class, for example, could be the basis for creating many Stack classes (e.g., "Stack of Double," "Stack of Integer," "Stack of Char," "Stack of Employee"). Figure 25.5 presents a generic Stack class declaration. A generic class declaration is similar to a non-generic class declaration, except that the class name is followed by a type parameter list (line 3). Type parameter E represents the element type the Stack will manipulate. As with generic methods, the type parameter list of a generic class can have one or more type parameters separated by commas. Type parameter E is used throughout the Stack class declaration (Fig. 25.5) to represent the element type. Class Stack declares variable elements as an array of type E (line 5). This array (created at line 15 or 17) will store the Stack's elements. [Note: This example implements a Stack as an array. As you have seen in Chapter 24, Data Structures, Stacks also are commonly implemented as linked lists.] Figure 25.5. Generic class Stack declaration 1 ' Fig. 25.5: Stack.vb 2 ' Generic class Stack 3 Public Class Stack(Of E) 4 Private top As Integer ' location of the top element 5 Private elements() As E ' array that stores Stack elements 6 7 ' parameterless constructor creates a Stack of the default size 8 Public Sub New() 9 MyClass. New(10)' default stack size 10 elements 10 End Sub ' New 11 12 ' constructor creates a Stack of the specified number of elements 13 Public Sub New(ByVal stackSize As Integer) 14 If stackSize > 0 Then ' validate stackSize 15 elements = New E(stackSize - 1) {} ' create stackSize elements 16 Else 17 elements = New E(9) {}' create 10 elements 18 End If 19 20 top = -1 ' Stack initially empty 21 End Sub' New 22 23 ' push element onto the Stack; if successful, return true 24 ' otherwise, throw FullStackException 25 Public Sub Push( ByVal pushValue As E) 26 If top = elements.Length - 1 Then' Stack is full 27 Throw New FullStackException(String.Format( _ 28 "Stack is full, cannot push {0}", pushValue)) 29 End If 30 31 top += 1 ' increment top 32 elements(top) = pushValue' place pushValue on Stack 33 End Sub' Push 34 35 ' return the top element if not empty 36 ' else throw EmptyStackException 37 Public Function Pop() As E 38 If top = -1 Then ' Stack is empty 39 Throw New EmptyStackException("Stack is empty, cannot pop") 40 End If 41 42 top -= 1 ' decrement top 43 Return elements(top + 1)' return top value 44 End Function' Pop 45 End Class' Stack | Class Stack has two constructors. The parameterless constructor (lines 810)passes the default stack size (10) to the one-argument constructor (line 9) by invoking the constructor in lines 1321. The one-argument constructor (lines 1321) validates the stackSize argument and creates an array of the specified stackSize if it is greater than0 or an array of 10 elements otherwise. Method Push (lines 2533) first determines whether an attempt is being made to push an element onto a full Stack. If so, lines 2728 throw a FullStackException (declared in Fig. 25.6). If the Stack is not full, line 31 increments the top counter to indicate the new top position, and line 32 places the argument in that location of array elements. Figure 25.6. FullStackException class declaration 1 ' Fig. 25.6: FullStackException.vb 2 ' Indicates a stack is full. 3 Public Class FullStackException : Inherits ApplicationException 4 ' parameterless constructor 5 Public Sub New() 6 MyBase.New("Stack is full") 7 End Sub' New 8 9 ' one-parameter constructor 10 Public Sub New( ByVal exception As String) 11 MyBase.New(exception) 12 End Sub ' New 13 End Class ' FullStackException | Method Pop (lines 3744) first determines whether an attempt is being made to pop an element from an empty Stack. If so, line 39 throws an EmptyStackException (declared in Fig. 25.7). Otherwise, line 42 decrements the top counter to indicate the new top position, and line 43 returns the original top element of the Stack. Figure 25.7. EmptyStackException class declaration 1 ' Fig. 25.7: EmptyStackException.vb 2 ' Indicates a stack is empty 3 Public Class EmptyStackException : Inherits ApplicationException 4 ' parameterless constructor 5 Public Sub New() 6 MyBase.New("Stack is empty") 7 End Sub' New 8 9 ' one-parameter constructor 10 Public Sub New( ByVal exception As String) 11 MyBase.New(exception) 12 End Sub' New 13 End Class' EmptyStackException | Classes FullStackException (Fig. 25.6) and EmptyStackException (Fig. 25.7) each provide a parameterless constructor and a one-argument constructor. The parameterless constructor sets the default error message, and the one-argument constructor sets a custom error message. As with generic methods, when a generic class is compiled, the compiler performs type checking on the class's type parameters to ensure that they can be used with the code in the generic class. The constraints determine the operations that can be performed on the type parameters. The runtime system replaces the type parameters with the actual types. For class Stack (Fig. 25.5), no type constraint is specified, so the default type constraint, Object, is used. The scope of a generic class's type parameter is the entire class. Now let's consider an application (Fig. 25.8) that uses the Stack generic class. Lines 89 declare variables of type Stack(Of Double) (pronounced "Stack of Double") and Stack(Of Integer) (pronounced "Stack of Integer"). The types Double and Integer are the type arguments. The compiler replaces the type parameters in the generic class with the type arguments so that the compiler can perform type checking. Method Main instantiates objects doubleStack of size5 (line 12) and integerStack of size 10 (line 13), then calls methods TestPushDouble (lines 2238), TestPopDouble (lines 4158), TestPushInteger (lines 6177) and TestPopInteger (lines 8097) to manipulate the two Stacks in this example. Figure 25.8. Generic class Stack test program 1 ' Fig. 25.8: StackTest.vb 2 ' Stack generic class test program. 3 Module StackTest 4 ' create arrays of doubles and integers 5 Dim doubleElements() As Double = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6} 6 Dim integerElements() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} 7 8 Dim doubleStack As Stack( Of Double) ' stack stores double objects 9 Dim integerStack As Stack( Of Integer)' stack stores integer objects 10 11 Sub Main() 12 doubleStack = New Stack( Of Double)(5)' Stack of doubles 13 integerStack = New Stack( Of Integer)(10)' Stack of integers 14 15 TestPushDouble()' push doubles onto doubleStack 16 TestPopDouble()' pop doubles from doubleStack 17 TestPushInteger()' push integers onto integerStack 18 TestPopInteger()' pop integers from integerStack 19 End Sub' Main 20 21 ' test Push method with doubleStack 22 Sub TestPushDouble() 23 ' push elements onto stack 24 Try 25 Console.WriteLine(vbCrLf & _ 26 "Pushing elements onto doubleStack") 27 28 ' push elements onto stack 29 For Each element As Double In doubleElements 30 Console.Write("{0:F1} ", element) 31 doubleStack.Push(element)' push onto doubleStack 32 Next element 33 Catch exception As FullStackException 34 Console.Error.WriteLine() 35 Console.Error.WriteLine("Message: " & exception.Message) 36 Console.Error.WriteLine(exception.StackTrace) 37 End Try 38 End Sub' TestPushDouble 39 40 ' test Pop method with doubleStack 41 Sub TestPopDouble() 42 ' pop elements from stack 43 Try 44 Console.WriteLine(vbCrLf & _ 45 "Popping elements from doubleStack") 46 47 Dim popValue As Double' store element removed from stack 48 ' remove all elements from Stack 49 While True 50 popValue = doubleStack.Pop()' pop from doubleStack 51 Console.Write("{0:F1} ", popValue) 52 End While 53 Catch exception As EmptyStackException 54 Console.Error.WriteLine() 55 Console.Error.WriteLine("Message: " & exception.Message) 56 Console.Error.WriteLine(exception.StackTrace) 57 End Try 58 End Sub' TestPopDouble 59 60 ' test Push method with integerStack 61 Sub TestPushInteger() 62 ' push elements onto stack 63 Try 64 Console.WriteLine(vbCrLf & _ 65 "Pushing elements onto integerStack") 66 67 ' push elements onto stack 68 For Each element As Integer In integerElements 69 Console.Write("{0} ", element) 70 integerStack.Push(element)' push onto integerStack 71 Next element 72 Catch exception As FullStackException 73 Console.Error.WriteLine() 74 Console.Error.WriteLine("Message: " & exception.Message) 75 Console.Error.WriteLine(exception.StackTrace) 76 End Try 77 End Sub' TestPushInteger 78 79 ' test Pop method with integerStack 80 Sub TestPopInteger() 81 ' pop elements from stack 82 Try 83 Console.WriteLine(vbCrLf & _ 84 "Popping elements from integerStack") 85 86 Dim popValue As Integer' store element removed from stack 87 ' remove all elements from Stack 88 While True 89 popValue = integerStack.Pop()' pop from integerStack 90 Console.Write("{0} ", popValue) 91 End While 92 Catch exception As EmptyStackException 93 Console.Error.WriteLine() 94 Console.Error.WriteLine("Message: " & exception.Message) 95 Console.Error.WriteLine(exception.StackTrace) 96 End Try 97 End Sub' TestPopInteger 98 End Module' StackTest | Method TestPushDouble (lines 2238) invokes method Push to place the Double values 1.1,2.2,3.3,4.4 and5.5 stored in array doubleElements onto doubleStack. The loop in lines 2932 terminates when the test program attempts to Push a sixth valueonto doubleStack (which is full, because doubleStack can store only five elements). In this case, the method throws a FullStackException (Fig. 25.6) to indicate that the Stack is full. Lines 3336 catch this exception, and print the message and stack-trace information (see the output of Fig. 25.8). The stack trace indicates the exception that occurred and shows that Stack method Push generated the exception in line 27 of the file Stack.vb (Fig. 25.5). The trace also shows that method Push was called by StackTestmethod TestPushDouble in line 31 of StackTest.vb. This information enables you to determine the methods that were on the method call stack at the time that the exception occurred. The program catches the exception, so the Visual Basic runtime environment considers the exception to have been handled, and the program can continue executing. Method TestPopDouble (lines 4158) invokes Stack method Pop in an infinite loop to remove all the values from the stack. Note in the output that the values are popped in last-in, first-out (LIFO) orderthis, of course, is the defining characteristic of stacks. The loop in lines 4952 continues until the stack is empty. An EmptyStackException occurs when an attempt is made to pop from the empty stack. This causes the program to proceed to the Catch block (lines 5356) and handle the exception, so that the program can continue executing. When the test program attempts to Pop a sixth value, the doubleStack is empty, so method Pop throws anEmptyStackException. Method TestPushInteger (lines 6177) invokes Stack method Push to place values onto integerStack until it is full. Method TestPopInteger (lines 8097) invokes Stack method Pop to remove values from integerStack until it is empty. Once again, note that the values pop off in last-in, first-out (LIFO) order. Creating Generic Methods to Test Class Stack(Of E) The code is almost identical in methods TestPushDouble and TestPushInteger for pushing values onto a Stack(Of Double) or a Stack(Of Integer), respectively. Similarly the code is almost identical in methods TestPopDouble and TestPopInteger for popping values from a Stack(Of Double) or a Stack(Of Integer), respectively. This presents another opportunity to use generic methods. Figure 25.9 declares generic method TestPush (lines 2542) to perform the same tasks as TestPushDouble and TestPushInteger in Fig. 25.8that is, Push values onto a Stack(Of E). Similarly, generic method TestPop (lines 4462) performs the same tasks as TestPopDouble and TestPopInteger in Fig. 25.8that is, Pop values off a Stack(Of E). Except for the slight differences in the stack traces, the output of Fig. 25.9 matches the output of Fig. 25.8. Figure 25.9. Passing a generic type Stack to a generic method 1 ' Fig 25.9: StackTest.vb 2 ' Stack generic class test program. 3 Module StackTest 4 ' create arrays of doubles and integers 5 Dim doubleElements() As Double = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6} 6 Dim integerElements() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} 7 8 Dim doubleStack As Stack( Of Double)' stack stores double objects 9 Dim integerStack As Stack( Of Integer)' stack stores integer objects 10 11 Sub Main() 12 doubleStack = New Stack( Of Double)(5)' Stack of doubles 13 integerStack = New Stack( Of Integer)(10)' Stack of integers 14 15 ' push doubles onto doubleStack 16 TestPush("doubleStack", doubleStack, doubleElements) 17 ' pop doubles from doubleStack 18 TestPop("doubleStack", doubleStack) 19 ' push integers onto integerStack 20 TestPush("integerStack", integerStack, integerElements) 21 ' pop integers from integerStack 22 TestPop("integerStack", integerStack) 23 End Sub' Main 24 25 Sub TestPush( Of E)( ByVal name As String, ByVal stack As Stack( Of E), _ 26 ByVal elements() As E) 27 ' push elements onto stack 28 Try 29 Console.WriteLine(vbCrLf & _ 30 "Pushing elements onto " & name) 31 32 ' push elements onto stack 33 For Each element As E In elements 34 Console.Write("{0} ", element) 35 stack.Push(element)' push onto stack 36 Next element 37 Catch exception As FullStackException 38 Console.Error.WriteLine() 39 Console.Error.WriteLine("Message: " & exception.Message) 40 Console.Error.WriteLine(exception.StackTrace) 41 End Try 42 End Sub' TestPush 43 44 Sub TestPop( Of E)( ByVal name As String, ByVal stack As Stack( Of E)) 45 ' pop elements off stack 46 Try 47 Console.WriteLine(vbCrLf & _ 48 "Popping elements from " & name) 49 50 Dim popValue As E' store element removed from stack 51 52 ' remove all elements from Stack 53 While True 54 popValue = stack.Pop()' pop from stack 55 Console.Write("{0} ", popValue) 56 End While 57 Catch exception As EmptyStackException 58 Console.Error.WriteLine() 59 Console.Error.WriteLine("Message: " & exception.Message) 60 Console.Error.WriteLine(exception.StackTrace) 61 End Try 62 End Sub' TestPop 63 End Module' StackTest [View full width] Pushing elements onto doubleStack 1.1 2.2 3.3 4.4 5.5 6.6 Message: Stack is full, cannot push 6.6 at Stack.Stack`1.Push(E pushValue) in C:\examples\ch25\Fig25_09\Stack\Stack.vb:line 27 at Stack.StackTest.TestPush[E](String name, Stack`1 stack, IEnumerable`1 elements) in C:\examples\ch25\Fig25_09\Stack\StackTest .vb:line 35 Popping elements from doubleStack 5.5 4.4 3.3 2.2 1.1 Message: Stack is empty, cannot pop at Stack.Stack`1.Pop() in C:\examples\ch25\Fig25_09\Stack\Stack.vb :line 39 at Stack.StackTest.TestPop[E](String name, Stack`1 stack) in C:\examples\ch25\Fig25_09\Stack\StackTest .vb:line 54 Pushing elements onto integerStack 1 2 3 4 5 6 7 8 9 10 11 Message: Stack is full, cannot push 11 at Stack.Stack`1.Push(E pushValue) in C:\examples\ch25\Fig25_09\Stack\Stack.vb :line 27 at Stack.StackTest.TestPush[E](String name, Stack`1 stack, IEnumerable`1 elements) in C:\examples\ch25\Fig25_09\Stack\StackTest .vb:line 35 Popping elements from integerStack 10 9 8 7 6 5 4 3 2 1 Message: Stack is empty, cannot pop at Stack.Stack`1.Pop() in C:\examples\ch25\Fig25_09\Stack\Stack.vb :line 39 at Stack.StackTest.TestPop[E](String name, Stack`1 stack) in C:\examples\ch25\Fig25_09\Stack\StackTest .vb:line 54
|
| Method Main (lines 1123) creates the Stack(Of Double) and Stack(Of Integer) objects (lines 1213) . Lines 1622 invoke generic methods TestPush and TestPop to test the Stack objects. Generic method TestPush (lines 2542) uses type parameter E (specified in line 25) to represent the type stored in the Stack. The generic method takes three argumentsa String that represents the name of the Stack object for output purposes, an object of type Stack(Of E) and an array of type E that contains the elements that will be Push edonto Stack(Of E). Note that the compiler enforces consistency between the type of the Stack and the elements that will be pushed onto the Stack when Push is invoked, which is the type argument of the generic method call. Generic method TestPop (lines 4462) takes two argumentsa String that represents the name of the Stack object for output purposes and an object of type Stack(Of E). |