繁体   English   中英

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

[英]Why does Java need interfaces and Smalltalk does not?

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

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

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

例如:

  • 在Java中,您需要实现Cloneable,它将方法Cloneable.clone定义为具有cloneble对象。 然后,编译器知道你的对象将理解该方法(否则会抛出错误)
  • 在smalltalk中,您只需要实现方法#clone。 在调用之前,编译器永远不会知道/不关心哪些消息理解您的对象。

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

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

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

请参阅 - 向Smalltalk添加动态接口

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

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

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

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


  1. Smalltalk只有一种主要类型

这意味着它不能只是警告你,如果你尝试将一个正方形放入圆孔,没有正方形,没有孔,一切都是smalltalk编译器的对象。

编译器可以选择输入已分配的演绎变量,但是这样做可以使用哲学对象。


  1. Smalltalk方法总是采用一个参数

看起来好像在做myArray at: 1 put: 'hi'有两个参数,但实际上,你正在调用myArray ['at:put:']([1,'hi'])的javascript等于myArray一个对象(~praymap)。 因此,如果不打破smalltalk的哲学,就无法检查参数的数量。

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


  1. smalltalk将其编译器暴露给运行时,而java则很难从运行时掩盖编译器。

当您将编译器暴露给运行时(从汇编到javascript的所有语言都可以轻松地将其编译器暴露给运行时,很少有人将其作为语言易于访问的部分的一部分,编译器在运行时可访问的越多,我们认为的更高级别)因为在编译时在一行上使用的信息在另一行上可能不再有效,因为在运行时,依赖于修复的信息编译器不再相同,因此您的语言变得更加脆弱。

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

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


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

当前的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可以很容易地摆脱接口,并使用反射多态,将所有东西都视为对象,但是在不让你按字符串索引的情况下与对象交谈以及为字符串重载索引运算符以动态查找成员将会更加冗长。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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