简体   繁体   中英

Why can't I override the #at:put: method in a class derived from the Dictionary-class?

I try to implement a specific Dictionary class in Smalltalk which needs to override the #at:put: method in Pharo and Squeak. But when I create a class having #at:put: as an instance method and I send that methode, I get the error:

Error: Instances of '#SortedDictionary' class are not indexable

The class definition is as follows:

Dictionary subclass: #SortedDictionary
   instanceVariableNames: 'index'
   classVariableNames: ''
   category: 'MyApplication'

An instance is created by overriding new:

!SortedDictionary class methodsFor: 'creation' stamp: 'nanitous 9/28/2015 19:17'!
new
    super new.
    ^self initialize! !

An instance is initialized with:

initialize
  index := Heap new.
  ^self

And the instance method is defined as:

at: anIndex put: aValue
  index add: anIndex.
  ^self at: anIndex put: aValue! !

And I test with the script in a workspace:

| d |
d := SortedDictionary new.
d at: 1 put: 3.

I tried to make a class not derived from #Dictionary but from #Object and used an instance variable dict containing an instance of #Dictionary , but with the same result.

Why can't I override #at:put: and and how can I override this method?

EDIT

Thanks to @lurker and @aka.nice I should have done the following:

!SortedDictionary class methodsFor: 'creation' stamp: 'nanitous 9/28/2015 19:17'!
new
    ^super new initialize! !

Doing this wrong was outright silly! In the original and wrong code I was trying to index a nil object.

And:

!SortedDictionary instance methodsFor: 'accessing' stamp: 'nanitous 9/28/2015 19:17'!
at: anIndex put: aValue
  index add: anIndex.
  ^super at: anIndex put: aValue! !

Well, I never came to solve this one before solving the #new issue.

Thanks again to everyone taking the trouble to help out here!

Generally, an instance of collection (more precisely a subclass of Collection) is created with #new:, not #new.

The parameter passed to new: is a size, either the size for a fixed size collection (like Array new: 3 ), or some pre-allocated size for variable size collection (like OrderedCollection , Set , Dictionary , ...).

From the stamp, I guess you are on a Squeak or Pharo flavour, so I will continue explanation with these dialects, it may slightly vary for other flavours.

In Squeak/Pharo, see the definition of HashedCollection class>>new:

new: nElements
    "Create a Set large enough to hold nElements without growing"
    ^ self basicNew initialize: (self sizeFor: nElements)

It sends initialize: not initialize. So the first thing you have to do is to define initialize: at instance side of your class, and the second thing is to remove definition of new/new: overriding these is rarely ever needed in Squeak/Pharo.

Currently you have a problem in your #new definition, when you tell self initialize what is self exactly? it is the class SortedDictionary , so you initialize the class, not the instance! And you answer the class, not the newly created instance, so you later send at:put: to the class...

It should have been something like newInstance := super new. ^newInstance initialize newInstance := super new. ^newInstance initialize .

Last, your at:put: definition will loop forever, it should invoke super at: ... put: ...

A couple of nits to pick.

When you write Smalltalk code as text, such as we're doing here,
you can use the format

{classname|blank} {class|blank} >> methodHead
where the first field names the class, the second field tells whether it is class side or instance side, and the '>>' indicates start of source code. If you don't name the class, we assume the same class as the last one named. If you don't say it is class side, we assume it is instance side. So your methods would be written as

\n\n    SortedDictionary class>>new \n        ^super new \n            initialize \n\n    >>initialize \n        index := Heap new \n\n    >>at: anIndex put: aValue \n        index add: anIndex. \n        ^super at: anIndex put: aValue \n\n

Second, since you're defining a subclass, you only need to define your own #new (and/or #new:) method if you must override the one that is otherwise inherited from the superclasses.
(But you knew that).

Third, whenever you write an #initialize method, you want to get in the habit of writing 'super initialize.' as the first line thereof.

And once you get into said habit, you'll want to get out of the habit of writing your #new methods starting with '^super new initialize', and get into the habit of starting them with 'self basicNew initialize' instead.

I know, everyone learns to do it that other way. (Sigh.)
But that is soooooo wrong.
Extra points if you can figure out why this is so. ;-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM