CSE 505 Lecture Notes: Metaclasses


All objects in Smalltalk are instances of some class. For example, we might have an instance p of the class Point. Or 3.14 is an instance of the class Float.

Classes in Smalltalk are objects themselves, and understand certain messages, just like any other object. One often-used message is new, to make a new instance. A variant is new: (used for e.g. the class Array). Another common kind of message is one that gets a constant, for example ColorValue blue or Float pi.

In the Smalltalk browser we can see methods that define class messages by clicking the "class" button instead of the "instance" button. Often built-in classes contain a category "examples" under the "class" messages.

Suppose we want to redefine the new message to Stacks so that it automatically initializes the stack. We can do this as follows:

new
   | s |
   s := super new.  "note we have to use super rather than self!"
   s setsize: 10.
   ^s
or more concisely:
new
   ^super new setsize: 10
We could also define:
new: n
   ^super new setsize: n
allowing us to redefine new as:
new
   ^self new: 10

Object Creation in Class-based languages

In Java, new objects are created using "new", which invokes the appropriate constructor.
   p =  new Point(10,20);
The constructor is similar to, but not the same as, an ordinary method.

Point is not itself a runtime object in Java. For example, you can't pass a class as a parameter to a method, or assign it to a variable. (Java's Reflection API does let you accomplish this, but in a less direct way -- see below.)

In Smalltalk-76, classes are runtime objects, and are part of the normal class-instance scheme. To make an uninitialized instance of the class Point:

  p := Point new.
p understands messages like "+" and "x" and "printOn:" The definitions of these are in the class Point, which is itself an object.

Rule: to look up a message name, look in your class. If it's there, use it; otherwise search up the superclass chain. In other words, go up exactly one instance link, and then up 0 or more superclass links.

What is the class Point? In Smalltalk-76 it is an instance of the class Class. Class is a subclass of Object, and an instance of itself. Class understands messages like "new", "instvars", and "compile:" Consequence: all classes understand the same messages!

Thus in Smalltalk-76 initialization must be done like this:

  p := Point new.
  p x: 10 y: 20.
or
  p := Point new x: 10 y: 20.

Metaclasses

In Smalltalk-80, the designers wanted to allow class-specific initialization messages, e.g.

  p := Point x: 10 y: 20.
To accomplish this, every class is an instance of a unique metaclass. "Point" is an instance of "Point class" (the metaclass for point). Point class defines messages such as x:y:. Another benefit of this is that it gives the programmer a place to hang constants and such, e.g. Float pi

(Aside: this is accomplished in Java via static methods.)

In the Smalltalk environment, the class/instance switch in the browser lets one switch between methods the instances understand and methods the class understand (i.e. methods defined in the class and methods defined in the metaclass).

The cost in confusion can be huge, though. A particular problem is that new programmers are hit over the head with this right away (unless the teacher glosses over it, which is the usual pedagogic technique).

Alternative: prototype-based system (just make copies of a prototypical object)

Reflective Programming

another use of metaclasses: reflective programming see e.g. "The Art of the Metaobject Protocol"

Metaobject protocol in CLOS: "an interface to the language that gives users the ability to incrementally modify the language's behavior and implementation, as well as the ability to write programs within the language"

Example of use in CLOS: overriding the way instances are stored -- for example, record-like structure vs. dictionary.

Java's Reflection API

See the Reflection API Tutorial for details.

At least in the instructor's opinion, the Java Reflection API is relatively weak -- it lets you accomplish (with great fanfare) things that are trivial in Smalltalk. It doesn't have the power of the CLOS metaobject protocol. But hey, it's better than C++.

To create an object with a no-argument constructor:

   String className = "Point";          // or whatever ...
   Object x;                      
   Class c = Class.forName(className);  // a static method, not a constructor
   x = c.newInstance();
To create an object using a constructor with arguments (quoted from the part of the Java tutorial dealing with creating new instances):
  1. Create a Class object for the object you want to create.
  2. Create a Constructor object by invoking getConstructor on the Class object. The getConstructor method has one parameter: an array of Class objects that correspond to the constructor's parameters.
  3. Create the object by invoking newInstance on the Constructor object. The newInstance method has one parameter: an Object array whose elements are the argument values being passed to the constructor.
Note that Java is having exactly the same problem that Smalltalk did regarding class-specific initialization methods. However, Java programmers don't encounter this issue until they start using the reflection API (which they won't do right away, plus they will assume it will be weird and wonderful, and so they won't be surprised).