8.1: Object
- Page ID
- 36372
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)For all intents and purposes, Object
is the root of the inheritance hierarchy. Actually, in Squeak the true root of the hierarchy is ProtoObject
, which is used to define minimal entities that masquerade as objects, but we can ignore this point for the time being.
Object can be found in the Kernel-Objects category. Astonishingly, there are some 400 methods to be found here (including extensions). In other words, every class that you define will automatically provide these 400 methods, whether you know what they do or not. Note that some of the methods should be removed and new versions of Squeak may remove some of the superfluous methods.
The class comment for the Object
states:
Object
is the root class for almost all of the other classes in the class hierarchy. The exceptions areProtoObject
(the superclass ofObject
) and its subclasses. ClassObject
provides default behaviour common to all normal objects, such as access, copying, comparison, error handling, message sending, and reflection. Also utility messages that all objects should respond to are defined here.Object
has no instance variables, nor should any be added. This is due to several classes of objects that inherit fromObject
that have special implementations (SmallInteger
andUndefinedObject
for example) or the VM knows about and depends on the structure and layout of certain standard classes.
If we begin to browse the method categories on the instance side of Object
we start to see some of the key behaviour it provides.
Printing
Every object in Smalltalk can return a printed form of itself. You can select any expression in a workspace and select the print it
menu: this executes the expression and asks the returned object to print itself. In fact this sends the message printString
to the returned object. The method printString
, which is a template method, at its core sends the message printOn:
to its receiver. The message printOn:
is a hook that can be specialized.
Object»printOn: is very likely one of the methods that you will most frequently override. This method takes as its argument a Stream
on which a String
representation of the object will be written. The default implementation simply writes the class name preceded by “a
” or “an
”. Object»printString returns the String
that is written.
For example, the class Browser
does not redefine the method printOn:
and sending the message printString
to an instance executes the methods defined in Object
.
Browser new printString → 'a Browser'
The class TTCFont
shows an example of printOn:
specialization. It prints the name of the class followed by the family name, the size and the subfamily name of the font as shown by the code below which prints an instance of the class.
Code \(\PageIndex{1}\) (Squeak): printOn: Redefinition
TTCFont»printOn: aStream aStream nextPutAll: 'TTCFont('; nextPutAll: self familyName; space; print: self pointSize; space; nextPutAll: self subfamilyName; nextPut: $)
TTCFont allInstances anyOne printString → 'TTCFont(BitstreamVeraSans 6 Bold)'
Note that the message printOn:
is not the same as storeOn:
. The message storeOn:
puts on its argument stream an expression that can be used to recreate the receiver. This expression is evaluated when the stream is read using the message readFrom:
. printOn:
just returns a textual version of the receiver. Of course, it may happen that this textual representation may represent the receiver as a self-evaluating expression.
A word about representation and self-evaluating representation. In functional programming, expressions return values when executed. In Smalltalk, messages (expressions) return objects (values). Some objects have the nice properties that their value is themselves. For example, the value of the object true
is itself i.e., the object true. We call such objects self-evaluating objects. You can see a printed version of an object value when you print the object in a workspace. Here are some examples of such self-evaluating expressions.
true → true 3@4 → 3@4 $a → $a #(123) → #(123)
Note that some objects such as arrays are self-evaluating or not depending on the objects they contain. For example, an array of booleans is self-evaluating whereas an array of persons is not. In Squeak 3.9, a mechanism was introduced (via the message isSelfEvaluating
) to print collections in their self-evaluating forms as much as possible and this is especially true for brace arrays. The following example shows that a dynamic array is self-evaluating only if its elements are:
{10@10 . 100@100} → {10@10 . 100@100} {Browser new . 100@100} → an Array(a Browser 100@100)
Remember that literal arrays can only contain literals. Hence the following array does not contain two points but rather six literal elements.
#(10@10 100@100) → #(10 #@ 10 100 #@ 100)
Lots of printOn:
method specializations implement self-evaluating behavior. The implementations of Point»printOn: and Interval»printOn: are self-evaluating.
Code \(\PageIndex{2}\) (Squeak): Self-Evaluation of Point
Point»printOn: aStream "The receiver prints on aStream in terms of infix notation." x printOn: aStream. aStream nextPut: $@. y printOn: aStream
Code \(\PageIndex{3}\) (Squeak): Self-Evaluation of Interval
Interval»printOn: aStream aStream nextPut: $(; print: start; nextPutAll: ' to: '; print: stop. step ∼= 1 ifTrue: [aStream nextPutAll: ' by: '; print: step]. aStream nextPut: $)
1 to: 10 → (1 to: 10) "intervals are self-evaluating"
Identity and equality
In Smalltalk, the message = tests object equality (i.e., whether two objects represent the same value) whereas == tests object identity (i.e., whether two expressions represent the same object).
The default implementation of object equality is to test for object identity:
Code \(\PageIndex{4}\) (Squeak): Object Equality
Object»= anObject "Answer whether the receiver and the argument represent the same object. If = is redefined in any subclass, consider also redefining the message hash." ↑ self == anObject
This is a method that you will frequently want to override. Consider the case of Complex
numbers:
(1 + 2 i) = (1 + 2 i) → true "same value" (1 + 2 i) == (1 + 2 i) → false "but different objects"
This works because Complex
overrides = as follows:
Code \(\PageIndex{5}\) (Squeak): Equality for Complex Numbers
Complex»= anObject anObject isComplex ifTrue: [↑ (real = anObject real) & (imaginary = anObject imaginary)] ifFalse: [↑ anObject adaptToComplex: self andSend: #=]
The default implementation of Object»∼= simply negates Object»=, and should not normally need to be changed.
(1+2i) ∼= (1+4i) → true
If you override =, you should consider overriding hash. If instances of your class are ever used as keys in a Dictionary
, then you should make sure that instances that are considered to be equal have the same hash value:
Code \(\PageIndex{6}\) (Squeak): Hash Must Be Reimplemented for Complex Numbers
Complex»hash "Hash is reimplemented because = is implemented." ↑ real hash bitXor: imaginary hash.
Although you should override = and hash
together, you should never override ==. (The semantics of object identity is the same for all classes.) == is a primitive method of ProtoObject
.
Note that Squeak has some strange behaviour compared to other Smalltalks: for example a symbol and a string can be equal. (We consider this be a bug, not a feature.)
#'lulu' = 'lulu' → true 'lulu' = #'lulu' → true
Class membership
Several methods allow you to query the class of an object.
class. You can ask any object about its class using the message class
.
1 class → SmallInteger
Conversely, you can ask if an object is an instance of a specific class:
1 isMemberOf: SmallInteger → true "must be precisely this class" 1 isMemberOf: Integer → false 1 isMemberOf: Number → false 1 isMemberOf: Object → false
Since Smalltalk is written in itself, you can really navigate through its structure using the right combination of superclass and class messages (see Chapter 12).
isKindOf: Object»isKindOf: answers whether the receiver’s class is either the same as, or a subclass of the argument class.
1 isKindOf: SmallInteger → true 1 isKindOf: Integer → true 1 isKindOf: Number → true 1 isKindOf: Object → true 1 isKindOf: String → false 1/3 isKindOf: Number → true 1/3 isKindOf: Integer → false
1/3 which is a Fraction
is a kind of Number
, since the class Number
is a superclass of the class Fraction
, but 1/3 is not a Integer
.
respondsTo: Object»respondsTo: answers whether the receiver understands the message selector given as an argument.
1 respondsTo: #, → false
Normally it is a bad idea to query an object for its class, or to ask it which messages it understands. Instead of making decisions based on the class of object, you should simply send a message to the object and let it decide (i.e., on the basis of its class) how it should behave.
Copying
Copying objects introduces some subtle issues. Since instance variables are accessed by reference, a shallow copy of an object would share its references to instance variables with the original object:
a1 := { { 'harry' } }. a1 → #(#('harry')) a2 := a1 shallowCopy. a2 → #(#('harry')) (a1 at: 1) at: 1 put: 'sally'. a1 → #(#('sally')) a2 → #(#('sally')) "the subarray is shared"
Object»shallowCopy is a primitive method that creates a shallow copy of an object. Since a2
is only a shallow copy of a1
, the two arrays share a reference to the nested Array
that they contain.
Object»shallowCopy is the “public interface” to Object»copy and should be overridden if instances are unique. This is the case, for example, with the classes Boolean
, Character
, SmallInteger
, Symbol
and UndefinedObject
.
Object»copyTwoLevel does the obvious thing when a simple shallow copy does not suffice:
a1 := { { 'harry' } } . a2 := a1 copyTwoLevel. (a1 at: 1) at: 1 put: 'sally'. a1 → #(#('sally')) a2 → #(#('harry')) "fully independent state"
Object»deepCopy makes an arbitrarily deep copy of an object.
a1 := { { { 'harry' } } } . a2 := a1 deepCopy. (a1 at: 1) at: 1 put: 'sally'. a1 → #(#('sally')) a2 → #(#(#('harry')))
The problem with deepCopy
is that it will not terminate when applied to a mutually recursive structure:
a1 := { 'harry' }. a2 := { a1 }. a1 at: 1 put: a2. a1 deepCopy → ... does not terminate!
Although it is possible to override deepCopy
to do the right thing, Object»copy offers a better solution:
Code \(\PageIndex{7}\) (Squeak): Copying Objects as a Template Method
Object»copy "Answer another instance just like the receiver. Subclasses typically override postCopy; they typically do not override shallowCopy." ↑self shallowCopy postCopy
You should override postCopy
to copy any instance variables that should not be shared. postCopy
should always do a super postCopy
.
Debugging
The most important method here is halt
. In order to set a breakpoint in a method, simply insert the message send self halt
at some point in the body of the method. When this message is sent, execution will be interrupted and a debugger will open to this point in your program. (See Chapter 6 for more details about the debugger.)
The next most important message is assert:
, which takes a block as its argument. If the block returns true
, execution continues. Otherwise an AssertionFailure
exception will be raised. If this exception is not otherwise caught, the debugger will open to this point in the execution. assert:
is especially useful to support design by contract. The most typical usage is to check non-trivial pre-conditions to public methods of objects. Stack»pop could easily have been implemented as follows:
Code \(\PageIndex{8}\) (Squeak): Checking a Pre-Condition
Stack»pop "Return the first element and remove it from the stack." self assert: [ self isEmpty not ]. ↑self linkedList removeFirst element
Do not confuse Object»assert: with TestCase»assert:, which occurs in the SUnit testing framework (see Chapter 7). While the former expects a block as its argument1, the latter expects a Boolean. Although both are useful for debugging, they each serve a very different intent.
Error handling
This protocol contains several methods useful for signaling run-time errors.
Sending self deprecated:
anExplanationString
signals that the current method should no longer be used, if deprecation has been turned on in the debug protocol of the preference browser. The String
argument should offer an alternative.
1 doIfNotNil: [ :arg | arg printString, ' is not nil' ] → SmallInteger(Object)»doIfNotNil: has been deprecated. use ifNotNilDo:
doesNotUnderstand:
is sent whenever message lookup fails. The default implementation, i.e., Object»doesNotUnderstand: will trigger the debugger at this point. It may be useful to override doesNotUnderstand:
to provide some other behaviour.
Object»error and Object»error: are generic methods that can be used to raise exceptions. (Generally it is better to raise your own custom exceptions, so you can distinguish errors arising from your code from those coming from kernel classes.)
Abstract methods in Smalltalk are implemented by convention with the body self subclassResponsibility
. Should an abstract class be instantiated by accident, then calls to abstract methods will result in Object»subclassResponsibility being evaluated.
Code \(\PageIndex{9}\) (Squeak): Signaling That a Method Is Abstract
Object»subclassResponsibility "This message sets up a framework for the behavior of the class' subclasses. Announce that the subclass should have implemented this message." self error: 'My subclass should have overridden ', thisContext sender selector printString
Magnitude
, Number
and Boolean
are classical examples of abstract classes that we shall see shortly in this chapter.
Number new + 1 → Error: My subclass should have overridden #+
self shouldNotImplement
is sent by convention to signal that an inherited method is not appropriate for this subclass. This is generally a sign that something is not quite right with the design of the class hierarchy. Due to the limitations of single inheritance, however, sometimes it is very hard to avoid such workarounds.
A typical example is Collection»remove: which is inherited by Dictionary
but flagged as not implemented. (A Dictionary
provides removeKey:
instead.)
Testing
The testing methods have nothing to do with SUnit testing! A testing method is one that lets you ask a question about the state of the receiver and returns a Boolean
.
Numerous testing methods are provided by Object
. We have already seen isComplex
. Others include isArray
, isBoolean
, isBlock
, isCollection
and so on. Generally such methods are to be avoided since querying an object for its class is a form of violation of encapsulation. Instead of testing an object for its class, one should simply send a request and let the object decide how to handle it.
Nevertheless some of these testing methods are undeniably useful. The most useful are probably ProtoObject»isNil
and Object»notNil
(though the Null Object2 design pattern can obviate the need for even these methods).
Initialize release
A final key method that occurs not in Object
but in ProtoObject
is initialize
.
Code \(\PageIndex{10}\) (Squeak): initialize as an Empty Hook Method
ProtoObject»initialize "Subclasses should redefine this method to perform initializations on instance creation"
The reason this is important is that in Squeak as of version 3.9, the default new
method defined for every class in the system will send initialize
to newly created instances.
Code \(\PageIndex{11}\) (Squeak): new as a Class-Side Template Method
Behavior»new "Answer a new initialized instance of the receiver (which is a class) with no indexable variables. Fail if the class is indexable." ↑ self basicNew initialize
This means that simply by overriding the initialize
hook method, new instances of your class will automatically be initialized. The initialize
method should normally perform a super initialize
to establish the class invariant for any inherited instance variables. (Note that this is not the standard behaviour of other Smalltalks.)
- Actually, it will take any argument that understands
value
, including aBoolean
. - Bobby Woolf, Null Object. In Robert Martin, Dirk Riehle and Frank Buschmann, editors, Pattern Languages of Program Design 3. Addison Wesley, 1998.