以下代码打印“false”:
a := 'aaa'.
b := a deepCopy.
Transcript show: (a == b).
我确实期望这种行为,我对此的解释是deepCopy返回一个新对象“b”,它是一个与“a”完全不同的对象,因为运算符“==”通过引用进行比较,结果为“false” 。这是对的吗?
但是,我不明白为什么以下代码产生“true”:
a := 'aaa'.
b := 'aaa'.
Transcript show: (a == b).
这里我们对两个不同的对象“a”和“b”做了两个赋值,除了它们包含相同的值之外,它们之间不应该有任何关系。但是,如果运算符“==”按引用而不是按值进行比较,为什么此比较的结果为“true”?
答案 0 :(得分:4)
在这两种情况下同样的误解是问题不是“会发生什么?”,而是“保证什么?”。关键是不能保证'aaa' == 'aaa'
,但编译器和VM可以自由地做这种事情。复制的情况也是如此;因为字符串是不可变的,我想没有什么可以说复制字符串不能返回相同的对象!
在你的第一个例子中,像往常一样,最好的老师就是形象。 #deepCopy
委托#shallowCopy
,在某些时候评估class basicNew: index
,并将字符复制到新对象中。因此,这个特定的实现将始终创建一个新对象。
答案 1 :(得分:2)
除了Sean DeNigris所说的,在第二种情况下比较为false
的原因是当你一起执行所有三个语句时,编译器想要聪明并且只有一次为true
创建对象,并为'aaa'
和a
分享对象。
如果将其放入一个方法 * :
,也会发生同样的情况b
Object subclass: #MyClassA
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassA methodsFor: 'testing' stamp: nil prior: nil!
testStrings
| a b |
a := 'aaa'
b := 'aaa'
^ a == b
! !
但如果它们采用不同的方法,不就会发生:
MyClassA testStrings " ==> true"
Object subclass: #MyClassB
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
a
| a |
a := 'aaa'
^ a
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
b
| b |
b := 'aaa'
^ b
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
testStrings
^ self a == self b
! !
这是因为在Squeak中,像stings这样的文字对象存储在它们定义的方法的方法对象中
* :从技术上讲,每次MyClassB testStrings " ==> false"
或DoIt
,即你只需按键击执行代码,就会被编译为Squeak中的一个方法。
答案 2 :(得分:0)
这是我从在线散布的免费Smalltalk书籍中得知的,但我找不到参考:
正如您所期望的那样,类的实例是内存中的唯一对象。 deepCopy 首先有意创建一个对象,然后在其中存储现有实例的副本。
然而,Smalltalk将数字,字符和字符串视为原始数据类型。当文字数据(也称为文字)被分配给变量时,它们首先针对用户不可见的本地范围字典进行检查,并保留文字以检查它们是否为已经添加到它。如果它们没有,它们将被添加到字典中,变量将指向字典字段。如果之前已分配了相同的文字数据,则新变量将仅指向包含相同文字的本地范围字典字段。这意味着分配了相同文字的两个或多个变量指向同一个字典字段,因此是相同的对象。这就是为什么你的问题中的第二个比较是真实的。