Smalltalk和Java中面向对象编程的主要区别是什么?

45

Smalltalk和Java中面向对象(OO)的主要区别是什么?

请注意,我是一名Java程序员,正在尝试通过探索Smalltalk来扩展自己的视野。目前,除了它比Java更加纯粹之外,我几乎不知道任何关于Smalltalk的知识。因此,我希望回答能够展示各种Java概念如何映射到相应的Smalltalk概念,并介绍在Java中不存在的Smalltalk概念。

7个回答

42

消息传递

Smalltalk 使用消息传递,而不是方法调用。这种区别微妙但极为强大。

一些术语:给定 foo bar: baz#bar: 是一个 选择器,foo 是被称为 #bar: 的消息的 接收者(# 表示符号,就像 Common Lisp 会说 'bar(或更合适的是 :bar)),而 baz 是一个 参数形参。当执行该行时,将发送带有参数 baz 的消息 #:bar:foo。到目前为止,这是相当正常的。在 Java 中,它看起来像 foo.bar(baz);

在 Java 中,运行时系统会确定 foo 的实际类型,找到最合适的方法并运行它。

在 Smalltalk 中,情况看起来 几乎 相同。当您向对象发送消息时,它会在其方法字典中搜索与消息选择器名称匹配的方法。如果找不到,则在其超类的方法字典中搜索,依此类推。非常正常的东西。

如果找不到任何匹配的方法,则将使用原始消息作为参数向自身发送 #doesNotUnderstand: 消息。(是的,消息发送也是一个对象。)但是,#doesNotUnderstand: 也只是一个方法。您可以覆盖它。

例如,您可以有一个对象,它响应某些消息,同时将其接收到的任何其他消息转发到某个委托对象。覆盖 #doesNotUnderstand:,然后您就有了一个代理,无需维护即可保持其协议与委托同步。

简单语法

不,我不是在开玩笑。Smalltalk的整个语法可能只有15行长。而Java语言规范(JLS)则不是这样的。为什么要关注呢?一个简单的语法使得分解一块代码变得简单。元编程!重构!

没有以下语法:

  • 条件语句:(n < 3) ifTrue: ['yes'] ifFalse: ['no']
  • for循环:1 to: 10 do: [:i | Transcript show: i asString]
  • try-catch:[i := i / 0] ifError: ['oops!']
  • try-finally:[i := i / 0] ensure: [stream close]

请注意所有的[] - 具有干净语法的一流闭包。


我认为Frank的观点是,当使用#doesNotUnderstand:时,发送的消息被实体化为一级对象。在Java中,这对应于覆盖InvocationHandler#invoke(Object, Method, Object[])。在Smalltalk中,这可以发生在任何对象上,在Java中只能发生在动态代理类上。除此之外,我没有看到方法调用和消息发送之间的区别。 - Lukas Renggli
确实如此。但是,“在这些有限的情况下,您可以使用#doesNotUnderstand:”和“您可以在任何地方使用#doesNotUnderstand:”之间存在着天壤之别。真正的问题是:与没有#doesNotUnderstand:的语言相比,#doesNotUnderstand:是否使语言更加严谨(按Felleisen的意义)?我认为它确实如此,但我不确定。 - Frank Shearar
2
Smalltalk使用消息传递,而不是方法调用。我们可以说方法调用是一种非常有限的消息传递形式吗? - user1720897
1
@user1720897 - 在我看来,方法或子程序调用是一种较低级别的构造,在确定消息是否可以被处理以及实现消息的方法所在位置后,Smalltalk确实使用它。在Java和大多数其他语言中,要调用的方法/子例程通常在编译时确定。在Smalltalk中,要调用的方法(非常高效地)在运行时确定。这并不意味着Smalltalk没有经过编译-它确实经过了编译,但编译过程创建的是消息发送,而不是创建子例程调用。 - Bob Jarvis - Слава Україні

19
  1. 对象模型。 在Smalltalk中,每个东西都是一个对象。Java有原始类型,如int和float,其表示和行为与复杂对象不同。
  2. 行为调用。 Smalltalk对象的行为是通过发送消息来调用的。Java具有方法,这些方法基本上是函数调用,目标对象是一个特殊的第一个参数,称为this
  3. 封装。 Smalltalk具有严格的封装性。对象的字段只能通过消息公开。相比之下,Java允许公共字段。
  4. 动态性。 Smalltalk非常动态。所有类型都在运行时被识别。类可以在运行时进行内省和修改(动态元编程!)。新类可以在运行时创建和实例化。Java具有静态类型检查以及运行时多态性。有内省和反射,但不能从运行中的程序内部修改类和对象。
  5. 语法。 Smalltalk没有语法。相反,它具有用于发送消息的简单一致的格式。Java,像其他C系列语言一样,具有复杂的语法。
  6. 环境。 大多数Smalltalk实现都提供了一个完整的、独立的、活跃的计算环境,带有基于图像的持久性。其中一些环境甚至可以在裸机上引导。JVM反过来通常依赖于底层操作系统进行线程、网络等。源代码必须输入到文本文件中,编译并显式加载到JVM中以执行。

1
(2) Java和Smalltalk调用实例方法的方式没有区别(不考虑#doesNotUnderstand:)。 (3) Java支持使用类加载器和反射API在运行时创建和更改类和对象。 - Lukas Renggli
4
@Lukas:“把#doesNotUnderstand:放在一边”可以改变很多事情。如果我们剪掉所有不同的东西,你不能指望别人认真对待你的话--当然,如果我们剪掉所有不同的东西,那当然会有相似之处。 :P - cHao
1
Smalltalk有语法,只是不太多。 - Dave Newton
1
@cHa 但是#doesNotUnderstand与“消息发送”无关。Python有类似的机制,但Python通过方法调用工作,就像Java一样。我不明白消息传递和方法调用之间的真正语义差异是什么,而这个答案并没有解释这一点。 - uhbif19
@uhbif19:语义上的区别在于消息更抽象。它不受特定函数运行的限制。除了打开排队消息的想法外,这也意味着如果您希望,可以有一个处理程序(比如说doesNotUnderstand)拦截消息,并且理想情况下,消息的发送者不知道或不关心是否发生了这种情况。您可以通过函数调用实现有限形式的“消息发送”,但是您会错过大部分好处。 - cHao
显示剩余2条评论

18

Java和Smalltalk之间的一个关键区别是,Smalltalk有一流的类(不是双关语)。

在Smalltalk中,类是一个对象。最接近static方法和变量的东西是类侧的方法和变量,正如Frank Shearer所提到的那样。

但是,当继承被使用时,这种差异就更加深刻了。在Java中,类侧继承不存在,而在Smalltalk中是可能的。

如果类A继承自B,并且您有abAB的实例,在Smalltalk中,b class继承自a class。在Java中,这种情况并非如此,因为a getClass()b getClass()返回的是Class的实例,它们彼此之间没有关系。

假设现在类 A 实现了单例模式:它有一个类级别的字段 instance 和一个获取器方法 instance。类 B 是另一个对象,有它自己的 instance 字段。因此,A instanceB instance 将返回不同的对象。
从面向对象的角度来看,这显然是Smalltalk和Java之间的主要区别之一。
其他区别包括存在元类、扩展方法、鸭子类型与静态类型、doesNotUnderstand 的具象化和其他一些使得在Smalltalk或Java中编码完全不同的东西。
当然,Smalltalk有闭包,而Java仍然缺乏。
另请参见 为什么Java不允许覆盖静态方法?

请注意,Java并非完全静态类型化,因为它会执行一些运行时检查。 - mathk
1
@mathk 不,Java是静态类型的。所有变量的类型在编译时都是已知的。你可能在想强制转换、动态分派或反射,但这些都不违反静态类型。 - Quelklef

12

链接现已失效 - 你知道有没有替代品? - andrewdotnich
链接又可以用了,我刚试过 :) - Gira

8

在IT技术领域,有一个在Java中不存在但近年来越来越流行的Smalltalk概念——blocks。Blocks是一种包含定义所在上下文的匿名函数,重要的是,blocks也是对象。实际上,Smalltalk缺乏任何内置的if-语句、for-循环或类似的东西,但通过消息传递和blocks,成功地实现了相同的效果。

object isBig ifTrue: [self runIntoObject:object] 
            ifFalse: [self katamariBall absorbObject:object].

1 to: 10 do: [:number | number print]

7

在Smalltalk中,一切皆为对象,而在Java中,像小整数这样的东西仍然不是一等公民对象。此外,继续谈论数字,在Smalltalk中,由于其纯面向对象的性质和强大的反射能力,我们永远不需要担心数字大小,例如整数是小型还是大型,以及当小整数溢出到大型时会发生什么。


5
当@Janko Mivšek说“everything”时,他真的是指每一件事。 :)
甚至在发送消息之前,你正在创建一个作为上下文的对象。
此外,在Smalltalk中没有访问修饰符(private/protected/public),某些Smalltalk实现中也没有包,并且在大多数Smalltalk实现中,包与Java的语义不同。
在Smalltalk中,没有像for、if、try/catch这样的控制结构……很酷的是,你不需要它们,因为在Smalltalk中有闭包块。
在Smalltalk中,没有静态成员,而是有Class对象(你可以向Class发送消息,也可以将Class保存在变量中)。
在Smalltalk中,没有嵌套类。
...

1
关于控制结构,卡尔·休伊特的《将控制结构视为传递消息的模式》(http://dspace.mit.edu/handle/1721.1/6272)是一篇真正的启发性文章。 - Frank Shearar
1
在Smalltalk中,你没有像控制结构这样的东西。Smalltalk编译器通常会对控制结构方法进行特殊处理 - 它们不是普通的方法。 - igouy
4
没错,但这只是一种优化。 - Frank Shearar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接