为什么Java需要接口而Smalltalk不需要?

时间:2011-11-01 20:55:01

标签: java interface smalltalk

我已经在Smalltalk中编程了一段时间,但我从来没有真正需要接口来实现任何东西。那为什么Java等语言不能摆脱接口?它只是Smalltalk还是其他语言不需要接口?

5 个答案:

答案 0 :(得分:24)

因为Java是静态类型而Smalltalk不是。当您不声明类型并且您的变量不会被类型化时,接口不会用于任何目的。但是在像Java这样的静态类型语言中,它们非常方便,因为它们允许您拥有一个变量,其类型由对象实现的方法而不是其类来定义。它让你更接近动态类型Smalltalk原生而不放弃类型检查的好处。

答案 1 :(得分:9)

这是一个多态问题:在Java中你有静态类型,因此你需要知道你的对象可以回答哪些消息......在Smalltalk(和其他非静态语言)中你只需要实现正确的方法来获得多态性。

例如:

  • 在Java中,您需要实现定义方法的Cloneable Cloneable.clone有克隆对象。然后,编译器知道 你的对象将理解该方法(否则它会抛出一个 错误)
  • 在smalltalk中,您只需要实现方法#clone。 编译器从不知道/不关心哪些消息理解您的消息 对象,直到它被调用。

这也意味着你可以拥有多态对象而不属于同一层次结构......多继承,mixins和其他方法(Pharo上存在特征)只是重用技术,而不是设计约束。

这种做事方式通常被称为“鸭子打字”......请参阅:http://en.wikipedia.org/wiki/Duck_typing

答案 2 :(得分:1)

你认为Smalltalk中的“接口”可能有用吗?

请参阅 - Adding Dynamic Interfaces to Smalltalk

答案 3 :(得分:0)

不确定你的问题究竟是什么(或者更确切地说,你最想回答哪个问题)但是看看Ruby。从我的理解来看,它比Java更接近于smalltalk。

如果我要回答关于为什么java需要接口的问题,我想我会说一些关于java是一种静态类型语言的东西,并且把这种哲学带到了java的作用,这就是接口的需要。有效的接口尝试在java中提供类似多重继承的东西而没有其他语言面临的多重继承问题(C ++我相信)。

答案 4 :(得分:0)

Java不需要接口。这是编译器选择支持而不是丢弃的东西。

在运行时,接口无法以任何语言强制执行,因为所有动态对象都是纯状态的结构体或纯状态的结构体,第一个成员是指向vtable的指针,映射整数到成员(通过数组)或成员的字符串(是字典/ hashmap)。这样做的结果是你总是可以改变vtable的索引值或hashmap的条目值,或者只是将vtable指针改为另一个vtable地址,或者只是非法的访问内存。

Smalltalk可以很容易地存储在课程编译时给出的信息,并且以它的方式实现,这就是smalltalk浏览器中的intellisense如何给出成员建议,但这实际上不会对smalltalk有所帮助。

smalltalk的语法存在一些限制接口使用的问题。

  1. Smalltalk只有一种主要类型
  2. 这意味着如果您尝试将正方形放入圆形孔中,没有正方形,没有孔,一切都是smalltalk编译器的对象,它就不能警告您。

    编译器可以选择输入已分配的演绎变量,但是要对哲学上的对象进行小型操作。

    1. Smalltalk方法总是采用一个参数
    2. 看起来好像myArray at: 1 put: 'hi'有两个参数,但实际上,你正在调用myArray等同于javascript [' at:put:']([1,&#39] ; hi'])myArray是一个对象(~shotmap)。因此,如果不打破smalltalk的哲学,就无法检查参数的数量。

      有一些变通办法可以用smalltalk检查参数的数量,但它不会带来太多好处。

      1. smalltalk将其编译器暴露给运行时,而java则很难从运行时掩盖编译器。
      2. 当您将编译器暴露给运行时(从汇编到javascript的所有语言都可以轻松地将其编译器暴露给运行时,很少有人将其作为语言易于访问的部分的一部分,编译器在运行时更容易访问,更高级别我们认为语言是这样的,你的语言变得有点脆弱,因为你在编译时在一行上使用的信息可能不再在另一行上有效,因为在运行时,依赖于修复的信息编译器不再是相同。

        这样做的一个结果是一个类可能在程序的某一点有一个接口,但在程序的一半,用户将类更改为另一个接口;如果用户想在编译时使用这个接口(在使用代码更改类之后),编译器需要更聪明地意识到那些不支持" .Greet()&#34的类;现在突然做,或不再做,或者说方法" .Greet()"和方法" .Foo()"已被换掉。

        接口在编译时很棒,但在运行时完全无法执行。对于那些想要在不需要重新启动程序的情况下改变代码行为的人来说这是个好消息,对类型安全纯粹主义者来说这是个可怕的消息 - 他们的理想根本无法在运行时强制执行,而无需在一定时间间隔内手动调用每个断言。 / p>

        1. 与C ++不同,smalltalk不使用数组用于vtable,而是使用从字符串到对象的映射。这意味着即使您确实知道该方法存在于您正在调用的类中,也无法将其优化为dispid,以便将来对此方法的调用使用数组偏移而不是散列来查找该方法。为了证明这一点,让我们使用javascript:

        2. 当前的smalltalk对象的行为类似于:

          var myIntVtbl = {     ' +':function(self,obj1){         return {lpVtbl:myIntVtbl,data:self.data + obj1.data};     } }; var myInt1 = {lpVtbl:myIntVtbl,data:2}; var myInt2 = {lpVtbl:myIntVtbl,data:5}; var myInt3 = myInt1 [' lpVtbl'] [' +'](myInt1,myInt2); var myInt4 = myInt3 [' lpVtbl'] [' +'](myInt3,myInt3); var myInt5 = myInt4 [' lpVtbl'] [' +'](myInt4,myInt4); 的console.log(myInt5);

          每次拨打+时,我们都必须哈希' +'从vtable词典中获取成员。 Java的工作方式类似,这就是为什么反编译器可以很容易地告诉方法的名称。

          编译器在知道接口时可以执行的一个优化是将字符串编译为dispid,如下所示:

          var myIntVtbl = [     function(self,obj1){         return {lpVtbl:myIntVtbl,data:self.data + obj1.data};     } ];

          var myInt1 = {lpVtbl:myIntVtbl,data:2}; var myInt2 = {lpVtbl:myIntVtbl,data:5}; var myInt3 = myInt1 [' lpVtbl'] [0](myInt1,myInt2); var myInt4 = myInt3 [' lpVtbl'] [0](myInt3,myInt3); var myInt5 = myInt4 [' lpVtbl'] [0](myInt4,myInt4); 的console.log(myInt5);

          据我所知,java和smalltalk都没有为类做这个,而C ++,C#(通过comvisible属性)做。

          总而言之,smalltalk可以使用接口,反过来变得更像PHP,但除了弱的保证之外,在编译时不会获得它的任何好处。

          同样,Java并不需要接口,它可以像smalltalk一样工作,条件是将java编译器公开给java以便更容易访问。为了感受这一点,你可以在所有当前java工具包附带的java和nashorn javascript引擎之间进行交互,并使用它的' eval函数作为运行时编译器。 Java可以很容易地摆脱接口,并使用反射多态,将所有东西都视为对象,但是在不让你按字符串索引的情况下与对象交谈并且重载字符串的索引运算符以动态地找到它会更加冗长成员。