In Object Oriented languages, the class is the definition of a kind of object. Classes are arranged in tree-like hierachies with the roots of the tree being very general and the leaves being most specific. So a dessert is a kind of food, and cake is a kind of dessert. Because the class serves as the definition of an object, it makes sense that objects are created by using the class as a template. In other words, classes are used to create objects of the class’s type.
All of the languages discussed so far, C++, Java, Smalltalk, and Objective C have the notion of a class. What is different is how the languages represent the class itself.
C++ has no runtime representation of a class at all. Instead, all class information is soaked up by the compiler and represented using standard functions and globals with the class name prepended to their “local” names. The way a programmer designates a function as belonging to a class is to declare it within the class’s declaration using the very overworked keyword ’static’.
class CPlusPlusClass : public BaseObject
{
public:
static void classMember(); // declare the function
…
}
void CPlusPlusClass::classMember() {…} // define the function
…
CPlusPlusClass.classMember(); // really just a long name for a global function
Ditto for class variables. There’s no such thing in C++ really. Instead the language fakes it by letting you declare the variable in the class declaration’s namespace and in the end what you’ve got is a global with a very long name. Since there’s no class object, there’s no “this” variable and no way to do anything the least bit object oriented in class methods without explicitly naming super classes.
There’s also no built-in way to abstract object creation. When one wishes to create an object in C++, one uses the ‘new’ operator in conjunction with the class name, which coincidentally is also the required name of the initialization function. So in C++ we write:
BaseObject *c = new CPlusPlusClass();
Notice that here – at the moment of object creation, it is necessary to reveal the actual type of the object. If one desires a more abstract method of instance creation, it is necessary to adopt a factory idiom. Of course, converting to a factory creation mechanism from the use of operator new requires extensive modification of code, similar to the extensive work required to handle a new kind of exception.
Smalltalk takes the opposite extreme. Classes are represented as regular objects and are arranged into an inheritance hierarchy. The special variable “self” refers to the class object itself and ’super’ refers to the object that represents the classes superclass. Smalltalk class methods exhibit full polymorphism and class variables are made available to class methods and instance methods in the class’s instances. The Smalltalk class takes responsibility for instance creation and is thus a factory by default.
For instance, in the Squeak implementation, ImageReadWriter is an abstract class for reading and writing image files. Concrete subclasses exist for JPEG, GIF, PNG and so forth. The abstract base class makes use of class hierarchy navigation to find the correct subclass to render the image data. It looks something like this (error handling omitted):
“Find the first subclass that claims to be able to understand the binary stream’s data”
readerClass := self withAllSubclasses detect: [:subclass | binaryStream reset. (subclass new on: binaryStream) understandsImageFormat].
“Instantiate a reader from the class”
reader := readerClass new on: (binaryStream reset).
“Return the image from the reader”
^reader nextImage.
The beauty of this approach is that one can always write a new subclass of ImageReadWriter for a new image format and the new format is instantly supported without having to deal with registries or factory idioms.
Techniques like this demonstrate the power of using the object paradigm to represent classes.
Objective C uses a similar approach to Smalltalk with a couple of odd constraints. Objective C class objects lack support for class member variables. Instead one fakes it with static (as in C variables with internal linkage) variables. Also, Objective C performs lazy loading of libraries which means that not all subclasses are necessarily present at any given time. So the subclasses trick is a little hard to perform.
On the other hand, Objective C uses the same object creation mechanism as Smalltalk. So its still possible that the actual class of the object is not the same as the class that created it for you. Thus, the power of using objects to represent classes is mostly preserved.
Java’s approach is closer to C++ than to Smalltalk. While Java has ‘objects’ of type java.lang.Class for representing classes, these ‘objects’ have more in common with C structs than with objects in any other sense. Object creation is not performed by the class. Rather Java slavishly copies C++’s use of operator ‘new’.
Java also makes use of a ’static’ keyword to denote ‘class methods’. But these ‘methods’ are actually nothing but functions with long names. In fact, its not possible to do anything polymorphic in a class method. There isn’t even a way to get ahold of the class object representing the class.
public class A
{
// something that doesn’t work – no ‘this’
static void printClassName1() { System.out.println(”"+this.class); }
// this doesn’t work either
static void printClassName2() { System.out.println(”"+getClass()); }
// this does – but in subclasses it will be wrong
static void printClassName3() { System.out.println(”"+A.class); }
}
So crippling is the decision to not provide proper class objects, that the Enterprise Java Bean designers found it necessary to simulate class objects with the notion of “Home” objects. The “Home” object is intended to provide the ability to find existing objects or create new ones for a given class. This is definitely behavior that would ordinarily be put into a class object.
If we had one.