18.6 Simple Classes

 < Free Open Study > 



18.6 Simple Classes

The definitions of newCounter and newResetCounter are identical except for the reset method in the latter. Of course, both of these definitions are so short anyway that this makes little difference, but if we imagine them stretching over many pages, as can easily happen in practice, it is clear that we would prefer to have some means for describing the common functionality in one place. The mechanism by which this is achieved in most object-oriented languages is called classes.

The class mechanisms in real-world object-oriented languages tend to be complex and loaded with features-self, super, visibility annotations, static fields and methods, inner classes, friend classes, annotations such as final and Serializable, etc., etc.[3] We'll ignore most of these here and focus our attention on the most basic aspects of classes: code reuse via inheritance, and the late binding of self. For the moment, let's consider just the former.

In its most primitive form, a class is simply a data structure holding a collection of methods that can either be instantiated to yield a fresh object or extended to yield another class.

Why can't we just reuse the methods from some counter object to build a reset counter? Simply because, in any particular counter object, the method bodies contain references to some particular record of instance variables. Clearly, if we want to be able to reuse the same code with a different record of instance variables, what we need to do is to abstract the methods with respect to the instance variables. This amounts to breaking our newCounter function above into two pieces, one that defines the method bodies with respect to an arbitrary record of instance variables,

   counterClass =     λr:CounterRep.       {get = λ_:Unit. !(r.x),        inc = λ_:Unit. r.x:=succ(!(r.x))};  counterClass : CounterRep  Counter 

and one that allocates a record of instance variables and supplies it to the method bodies to create an object:

   newCounter =     λ_:Unit. let r = {x=ref 1} in                 counterClass r;  newCounter : Unit  Counter 

The method bodies from counterClass can be reused to define new classes, called subclasses. For example, we can define a class of reset counters:

   resetCounterClass =     λr:CounterRep.       let super = counterClass r in         {get   = super.get,          inc   = super.inc,          reset = λ_:Unit. r.x:=1};  resetCounterClass : CounterRep  ResetCounter 

Like counterClass, this function takes a record of instance variables and returns an object. Internally, it works by first using counterClass to create a counter object with the same record of instance variables r; this "parent object" is bound to the variable super. It then builds a new object by copying the get and inc fields from super and supplying a new function as the value for the reset field. Since super was built on r, all three methods share the same instance variables.

To build a reset counter object, we again just allocate memory for its instance variables and call resetCounterClass, where the real work happens.

   newResetCounter =     λ_:Unit. let r = {x=ref 1} in resetCounterClass r;  newResetCounter : Unit  ResetCounter 

18.6.1 Exercise [Recommended, ⋆⋆]

Write a subclass of resetCounterClass with an additional method dec that subtracts one from the current value stored in the counter. Use the fullref checker to test your new class.

18.6.2 Exercise [⋆⋆ ]

The explicit copying of most of the superclass fields into the record of subclass methods is notationally rather clumsy-it avoids explicitly re-entering the code of superclass methods in subclasses, but it still involves a lot of typing. If we were going to develop larger object-oriented programs in this style, we would soon wish for a more compact notation like "super with {reset = λ_:Unit. r.x:=1}," meaning "a record just like super but with a field reset containing the function λ_:Unit. r.x:=1. Write out syntax, operational semantics, and typing rules for this construct.

We should emphasize that these classes are values, not types. Also we can, if we like, create many classes that generate objects of exactly the same type. In mainstream object-oriented languages like C++ and , classes have a more complex status-they are used both as compile-time types and as run-time data structures. This point is discussed further in §19.3.

[3]The main reason for all this complexity is that, in most of these languages, classes are the only large-scale structuring mechanism. Indeed, there is just one widely used language-OCaml-that provides both classes and a sophisticated module system. So classes in most languages tend to become the dumping ground for all language features that have anything to do with large-scale program structure.



 < Free Open Study > 



Types and Programming Languages
Types and Programming Languages
ISBN: 0262162091
EAN: 2147483647
Year: 2002
Pages: 262

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net