previous up next     contents index
Next: Creating Classes Up: Classes and Objects Previous: Classes and Objects

An Example

Classes are defined using the class syntactic form. Here are four class definitions:

  (define stack% 
    (class '() ()
        (private 
         [stack '()]) ; A private instance variable
        (public 
         [name 'stack] ; A public instance variable 
         [push! (lambda (v) 
                  (set! stack (cons v stack)))] 
         [pop! (lambda () 
                 (let ([v (car stack)]) 
                   (set! stack (cdr stack)) 
                    v))]
         [empty? (lambda () 
                   (null? stack))] 
         [print-name (lambda () 
                       (display name) (newline))])))

 (define named-stack% 
    (class stack% (stack-name)
       (public 
         [name stack-name])
       (sequence
        (super-init))))

 (define double-stack% 
    (class stack% ()
        (inherit push!) 
        (public 
         [name 'double-stack]
         [double-push! (lambda (v) 
                         (push! v)
                         (push! v))])
        (sequence (super-init))))

  (define safe-stack%
    (class stack% ()
        (inherit empty?)
        (rename [std-pop! pop!])
        (public 
         [name 'safe-stack]
         [pop! (lambda ()
                 (if (empty?) #f (std-pop!)))])
        (sequence (super-init))))

Here, the class stack%gif defines the usual stack object with push!, pop!, and empty? methods. The class named-stack% is derived from stack%; it defines a stack that is named through the initialization argument. The call (super-init) causes the superclass's instance variables to be initialized. The class double-stack% extends the functionality stack% with a new method, double-push!. The class safe-stack% overrides the pop! method of stack%, insuring that #f is returned whenever the stack is empty.

In each of these classes, the instance variable name contains the name of the class. The name declarations in named-stack%, double-stack%, and safe-stack% override the declaration in stack%. Thus, when the print-name method of an object from double-stack% is invoked, the name printed to the screen is ``double-stack''.

While all of named-stack%, double-stack%, and safe-stack% inherit the push! method of stack%, it is declared with inherit only in double-stack% because named-stack% and safe-stack% do not need to use push!, so the inheritance does not need to be declared. Similarly, only safe-stack% needs to declare (inherit empty?).

The safe-stack% class overrides pop! to extend the implementation of pop!. Thus, the new definition of pop! must access the original pop! method that is defined in stack%. The rename declaration binds a new name, std-pop! to the original pop!. Then, std-pop! is used in the overriding pop!.

The make-object procedure creates an object from a class; additional arguments to make-object are passed on as initialization arguments. Here are some object creations using the classes defined above:

 
   (define stack (make-object stack%)) 
   (define fred (make-object named-stack% 'Fred)) 
   (define joe (make-object named-stack% 'Joe)) 
   (define double-stack (make-object double-stack%))
   (define safe-stack (make-object safe-stack%)) 

Note that an extra argument is given to make-object for the named-stack% class because named-stack% requires one initialization argument (the stack's name).

The ivar and send forms are used to access the instance variables of an object. ivar looks up a variable by name. send uses ivar to extract a variable's value, which should be a procedure; it then applies the procedure to arguments. For example, here is a simple expression that uses the objects created above:

 
   ((ivar stack push!) fred) ; or (send stack push! fred)
   (send stack push! double-stack)
   (let loop () 
     (if (not (send stack empty?)) 
         (begin 
           (send (send stack pop!) print-name) 
           (loop))))

This loop displays 'Fred and 'double-stack to the standard output port.

Although named-stack% has overridden the name declaration of stack%, the stack%-defined value is still present in fred and joe. We can access the value defined in stack% by specifying a class with ivar:

 
   (ivar fred name) ; => Fred
   (ivar stack% fred name) ; => stack 

previous up next     contents index
Next: Creating Classes Up: Classes and Objects Previous: Classes and Objects

PLT