为什么我不能覆盖从 Dictionary-class 派生的 class 中的 #at:put: 方法?

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

我尝试在 Smalltalk 中实现一个特定的字典 class,它需要覆盖 Pharo 和 Squeak 中的#at:put: 方法。但是,当我创建一个具有 #at:put: 的 class 作为实例方法并发送该方法时,出现错误:

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

class定义如下:

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

一个实例是通过覆盖 new 创建的:

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

实例初始化为:

initialize
  index := Heap new.
  ^self

实例方法定义为:

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

然后我在工作区中使用脚本进行测试:

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

我试图创建一个 class 不是 派生自 #Dictionary 但来自 #Object 并使用实例变量 dict包含 #Dictionary 的实例,但结果相同。

为什么我不能覆盖 #at:put: 以及如何覆盖此方法?

编辑

感谢@lurker 和@aka.nice 我应该做以下事情:

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

做错这件事简直太愚蠢了!在原始和错误的代码中,我试图索引一个 nil 对象。

并且:

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

好吧,在解决#new问题之前,我从来没有来解决这个问题。

再次感谢大家不厌其烦的帮忙!

通常,集合的实例(更准确地说是集合的子class)是使用#new: 而不是#new 创建的。

传递给 new: 的参数是一个大小,可以是固定大小集合的大小(如 Array new: 3),也可以是可变大小集合的一些预分配大小(如 OrderedCollectionSet, Dictionary, ...).

从图章来看,我猜你是 Squeak 或 Pharo 口味的,所以我将继续用这些方言解释,其他口味可能略有不同。

在Squeak/Pharo中,见HashedCollection的定义class>>new:

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

它发送初始化:不初始化。 所以你要做的第一件事是定义初始化:在你的 class 的实例端,第二件事是删除 new/new 的定义:在 [=35= 中很少需要覆盖这些].

目前您的#new 定义有问题,当您告诉 self initialize self 到底是什么时?它是 class SortedDictionary,所以你初始化 class,而不是实例!你回答 class,而不是新创建的实例,所以你稍后发送 at:put: 到 class...

应该是 newInstance := super new. ^newInstance initialize

最后,您的 at:put: 定义将永远循环,它应该调用 super at: ... put: ...

几尼特可供选择。

当您将 Smalltalk 代码编写为文本时,就像我们在这里所做的那样,
你可以使用格式

    {classname|blank} {class|blank} >> methodHead
其中第一个字段命名为 class,第二个字段表示它是 class 端还是实例端,'>>' 表示源代码的开始。 如果您不命名 class,我们假设与最后一个命名的 class 相同。 如果你不说是class端,我们就认为是实例端。 所以你的方法会写成


    SortedDictionary class>>new
        ^super new
            initialize

    >>initialize
        index := Heap new

    >>at: anIndex put: aValue
        index add: anIndex.
        ^super at: anIndex put: aValue

其次,由于您正在定义一个子class,您只需要定义 你自己的#new (and/or #new:) 方法,如果你必须覆盖从 superclasses 继承的方法。
(但你知道的)。

第三,每当你写一个#initialize方法时,你要养成在第一行写'super initialize.'的习惯。

一旦你养成了上述习惯,你就会想摆脱以“^super new initialize”开头编写#new 方法的习惯,养成以 [=35 开头的习惯=] 代替。

我知道,每个人 都学会了以其他方式做到这一点。 (叹气。)
但那是错误的。
如果您能弄清楚为什么会这样,请加分。 ;-)