360 likes | 431 Views
Advanced Object Oriented Systems. (CM0318) Lecture 8 (Last updated 15th February 2002). Purpose of Lectures 8 & 9. To illustrate why Smalltalk is different, in particular because: implementation of system classes is readily available
E N D
Advanced Object Oriented Systems (CM0318) Lecture 8 (Last updated 15th February 2002)
Purpose of Lectures 8 & 9 • To illustrate why Smalltalk is different, in particular because: • implementation of system classes is readily available • debugging tends to involve understanding system classes as well as one’s own • Smalltalk is a highly dynamic system
Topics • Implementation of collections • Introspection and dynamic modification of classes/objects • The Model-View-Controller paradigm • Debugging strategies • (Implementation & introspection are the topics of the present lecture)
Partial Collection hierarchy ProtoObject Object at: at:put: Collection includes: isEmpty add: remove: do: size Bag SequenceableCollection , first at:ifAbsent: indexOf: ArrayedCollection Array (“Variable subclass”) String < = beginsWith: Symbol OrderedCollection Set Dictionary at: at:put: includesKey:
Recommended Smalltalk reading • Byte, August 1981 (dedicated to Smalltalk) • Goldberg, A., and Robson, D., ‘Smalltalk-80 : the language’, Addison-Wesley, 1989. • Goldberg, A., ‘Smalltalk-80 : the interactive programming environment’, Addison-Wesley, 1984.
Implementation of Collections • NB it’s essential to annotate these handouts - otherwise the code won’t make much sense!! • You can verify all of what I am telling you in this lecture simply by browsing in Squeak - indeed, the main message is the ‘openness’ of the Smalltalk code. We’ll see how this affects debugging, etc., later.
Implementation of Array • Creation method: class method new:, which is actually inherited from Behavior(Remember Array is itself an object; instance of its metaclass, which is a subclass - with a few intermediaries in between - of Behavior).
Array (ctd.) • Main accessing methods: at: (implemented in Object), at:put: (implemented in Object), size (implemented in ArrayedCollection) • Example of code to replace every element in an array by that element + 1: a ← #(3 4 5). 1 to: a size do: [:i|a at: i put: (a at: i) + 1]. ^a
Array (ctd.) • Source code for Object>>at:put: at: index put: value "Primitive. Assumes receiver is indexable. Store the argument value in the indexable element of the receiver indicated by index. Fail if the index is not an Integer or is out of bounds. Or fail if the value is not of the right type for this kind of collection. Answer the value that was stored. Essential. See Object documentation whatIsAPrimitive." <primitive: 61> index isInteger ifTrue: [self class isVariable ifTrue: [(index >= 1 and: [index <= self size]) ifTrue: [self errorImproperStore] ifFalse: [self errorSubscriptBounds: index]] ifFalse: [self error: (self class name) , 's are not indexable']]. index isNumber ifTrue: [^self at: index asInteger put: value] ifFalse: [self errorNonIntegerIndex]
Array (ctd.) • Methods like at:, at:put: are defined in Object because there are 2 fundamental types of class: • Fixed (named instance variables only) • Defined <class> subclass: #<classname> ... • Variable (named instance variables and subscripted instance variables) • Defined <class> variableSubclass: #<classname> ...
Inst var n-1 Inst var n-1 Inst var 1 Inst var 2 Inst var n Inst var 3 Inst var 4 Inst var 5 Inst var n Inst var 1 Inst var 2 Inst var 3 Inst var 4 Inst var 5 1 2 3 m FIXED VARIABLE
Array (ctd.) • Testing for equality (inherited from SequenceableCollection) = otherCollection "Answer true if the receiver is equivalent to the <otherCollection>. First test for identity, then rule out different species and sizes of collections. As a last resort, examine each element of the receiver and the <otherCollection>." | size | self == otherCollection ifTrue: [^ true]. (self species == otherCollection species) ifFalse: [^ false]. (size ← self size) = otherCollection size ifFalse: [^ false]. 1 to: size do: [:index | (self at: index) = (otherCollection at: index) ifFalse: [^ false]]. ^ true
Array (ctd.) • Testing for inclusion (inherited from Collection): includes: anObject "Answer whether anObject is one of the receiver's elements." self do: [:each | anObject = each ifTrue: [^true]]. ^false
Array (ctd.) • Iteration - do: (inherited from SequenceableCollection) do: aBlock "Refer to the comment in Collection|do:." 1 to: self size do: [:index | aBlock value: (self at: index)]
OrderedCollection • Similar to Vector in Java • Comprises an Array, a firstIndex and a lastIndex (only part of the array is used). So an ordered collection with elements 34, 46, 25 might be represented thus: Element no Contents 1 ? 2 ? 3 34 <- firstIndex=3 4 46 5 25 <- lastIndex=5 6 ? 7 ?
Compared with Vector ... • Allows the collection to grow at the beginning and the end.
OrderedCollection example oc ← #(3 4 5) asOrderedCollection. oc addFirst: 'ho'. oc addLast: 'hoho'. ^oc at: 2 • Result is 3.
OrderedCollection (ctd.) • Instance creation (class methods): new ^self new: 10 new: anInteger "If a subclass adds fields, then it is necessary for that subclass to reimplement new:." ^ super new setCollection: (Array new: anInteger)
OrderedCollection (ctd.) • Instance creation (instance methods): setCollection: anArray array ← anArray. self reset reset firstIndex ← array size // 3 max: 1. lastIndex ← firstIndex - 1
OrderedCollection (ctd.) • Accessing using at: at: anInteger "Answer my element at index anInteger. at: is used by a knowledgeable client to access an existing element" (anInteger < 1 or: [anInteger + firstIndex - 1 > lastIndex]) ifTrue: [self errorNoSuchElement] ifFalse: [^ array at: anInteger + firstIndex - 1]
OrderedCollection (ctd.) • Adding a new element: addLast: newObject "Add newObject to the end of the receiver. Answer newObject." lastIndex = array size ifTrue: [self makeRoomAtLast]. lastIndex ← lastIndex + 1. array at: lastIndex put: newObject. ^ newObject
OrderedCollection (ctd.) • Growing ... makeRoomAtLast | newLast delta | newLast ← self size. array size - self size = 0 ifTrue: [self grow]. (delta ← firstIndex - 1) = 0 ifTrue: [^ self]. "we might be here under false premises or grow did the job for us" 1 to: newLast do: [:index | array at: index put: (array at: index + delta). array at: index + delta put: nil]. firstIndex ← 1. lastIndex ← newLast
OrderedCollection (ctd.) • More growing: grow "Become larger. Typically, a subclass has to override this if the subclass adds instance variables." | newArray | newArray ← Array new: self size + self growSize. newArray replaceFrom: 1 to: array size with: array startingAt: 1. array ← newArray
Exercise (unassessed) • Investigate: • Other methods implemented by/inherited by Array and OrderedCollection • The implementation of the Set class
Introspection; dynamic modification to classes/objects • Java provides limited introspection via the reflection API. Main things you can do: • determine an object’s class • get information about a class’ fields, methods, etc. • create an instance of a class whose name is only determined at run-time • get and set the value of an object’s field • invoke a method on an object • these things can be achieved even if the method name, for example, is only determined at run-time
Suggested reading • Tutorial on Java reflection: http://web2.java.sun.com/docs/books/tutorial/reflect/index.html
Example: invoking a method import java.lang.reflect.*; class SampleInvoke { public static void main(String[] args) { String firstWord = "Hello "; String secondWord = "everybody."; String bothWords = append(firstWord, secondWord); System.out.println(bothWords); }
public static String append(String firstWord, String secondWord) { String result = null; Class c = String.class; Class[] parameterTypes = new Class[] {String.class}; Method concatMethod; Object[] arguments = new Object[] {secondWord}; try { concatMethod = c.getMethod("concat", parameterTypes); result = (String) concatMethod.invoke(firstWord, arguments); } catch (NoSuchMethodException e) { System.out.println(e); } catch (IllegalAccessException e) { System.out.println(e); } catch (InvocationTargetException e) { System.out.println(e); } return result; } }
Doing the same in Smalltalk meth ← #,. ^'Hello ' perform: meth with: 'everybody.'
Finding an object’s class • Simply send it the message class • Example: t ← Test new. ^t class • Result: Test
Finding out if an object responds to a message • Send respondsTo: to an object or canUnderstand: to a class. E.g. o ← 'hello'. ^o respondsTo: #first Collection canUnderstand: #size
Implementation of canUnderstand: canUnderstand: selector "Answer whether the receiver can respond to the message whose selector is the argument. The selector can be in the method dictionary of the receiver's class or any of its superclasses." (self includesSelector: selector) ifTrue: [^true]. superclass == nil ifTrue: [^false]. ^superclass canUnderstand: selector • (Implemented in class Behaviour)
Finding all messages to which it responds • Collection allSelectors • Returns a Set of Symbols: • Set (rootStubInImageSegment: hash copyAddedStateFrom: propertyList anyOne cCode: hashMappedBy: caseOf:otherwise: wantsSteps truncated ifNil:ifNotNil: asBag respondsTo: species detectMax: caseOf: initialDeepCopierSize range confirm: adaptToInteger:andSend: ~~ readDataFrom:size: windowActiveOnFirstClick * perform:with: finalize comeFullyUpOnReload: primitiveError: deepCopy ...
Compiling a new method into a class • Suppose that we have a class Test. Then evaluating: Test compile: 'double: aNumber ^aNumber * 2’ creates a new method for Test on the fly! So now we can do: Test new double: 42 and get the result 84.
Individual object behaviour • Consider the following class definition: Object subclass: #Test instanceVariableNames: 'meth ' classVariableNames: '' poolDictionaries: '' category: 'Kernel-Objects' • Instance methods: setSpecialMethod: aBlock meth ← aBlock specialMethod: aValue ^meth value: aValue
Indiv. object behaviour (ctd.) • Then can create objects that behave differently. E.g. t1 ← Test new. t1 setSpecialMethod: [:i | i * 2]. t2 ← Test new. t2 setSpecialMethod: [:i| i - 1]. ^Array with: (t1 specialMethod: 42) with: (t2 specialMethod: 42) • Result is: (84 41 )