16.4: Classes, Method Dictionaries and Methods
- Page ID
- 39670
Since classes are objects, we can inspect or explore them just like any other object.
Evaluate Point inspect
.
In Figure \(\PageIndex{1}\), the inspector shows the structure of class Point
. You can see that the class stores its methods in a dictionary, indexing them by their selector. The selector #*
points to the decompiled bytecode of Point>>*
.
Let us consider the relationship between classes and methods. In Figure \(\PageIndex{2}\) we see that classes and metaclasses have the common superclass Behavior
. This is where new
is defined, amongst other key methods for classes. Every class has a method dictionary, which maps method selectors to compiled methods. Each compiled method knows the class in which it is installed. In Figure \(\PageIndex{1}\) we can even see that this is stored in an association in literal6
.
We can exploit the relationships between classes and methods to pose queries about the system. For example, to discover which methods are newly introduced in a given class, i.e., do not override superclass methods, we can navigate from the class to the method dictionary as follows:
[:aClass| aClass methodDict keys select: [:aMethod | (aClass superclass canUnderstand: aMethod) not ]] value: SmallInteger >>> an IdentitySet(#threeDigitName #printStringBase:nDigits: ...)
A compiled method does not simply store the bytecode of a method. It is also an object that provides numerous useful methods for querying the system. One such method is isAbstract
(which tells if the method sends subclassResponsibility
). We can use it to identify all the abstract methods of an abstract class.
[:aClass| aClass methodDict keys select: [:aMethod | (aClass>>aMethod) isAbstract ]] value: Number >>> an IdentitySet(#storeOn:base: #printOn:base: #+ #- #* #/ ...)
Note that this code sends the >>
message to a class to obtain the compiled method for a given selector.
To browse the super-sends within a given hierarchy, for example within the Collections hierarchy, we can pose a more sophisticated query:
class := Collection. SystemNavigation default browseMessageList: (class withAllSubclasses gather: [:each | each methodDict associations select: [:assoc | assoc value sendsToSuper] thenCollect: [:assoc | RGMethodDefinition realClass: each selector: assoc key]]) name: 'Supersends of ', class name, ' and its subclasses'
Note how we navigate from classes to method dictionaries to compiled methods to identify the methods we are interested in. A RGMethodDefinition
is a lightweight proxy for a compiled method that is used by many tools. There is a convenience method CompiledMethod>>methodReference
to return the method reference for a compiled method.
(Object>>#=) methodReference selector >>> #=