My studying notes for Java,Ruby,Ajax and other any interesting things.

星期五, 十一月 24, 2006

动态类型

在读programming ruby 2nd的时候读到其中有个"duck typing"不太明白什么意思,搜了一下,原来就是大名鼎鼎的动态类型的一个称呼:

动态类型

Java具有静态类型。开发人员可以声明每个变量的类型,然后在编译过程中,如果使用类型错误的变量,就会出现错误信息。另一方面,Ruby具有动态类型:不必声明变量或者函数的类型;只有在运行时,才会出现类型检查;如果调用的方法并不存在,就会出现错误。即便这样,Ruby也并不在乎对象的类,只关心它拥有的方法是不是含有方法调用里面所用的名字。正因为如此,动态方法得到了鸭子类型(duck typing)的名称:"如果它走起来像鸭子、叫起来像鸭子,那么它就是鸭子。"

代码片段1,鸭子类型:

class ADuck

def quack()

 puts "quack A";

end

end

class BDuck

def quack()

 puts "quack B";

end

end


# quack_it并不在乎变量duck的类型,只要它含有名为quack的方法。类A和类了没有继承关系。

def quack_it(duck)

duck.quack

end

a = ADuck.new

b = BDuck.new

quack_it(a)

quack_it(b)


Java还可以让开发人员使用反射(reflection)对动态类型进行归档,不过这种笨拙而繁琐的变通方法会带来让人混淆的异常,譬如NoSuchMethodError和InvocationTargetException,实际上,这些异常往往会出现在反射的Java代码中,其出现频率远超过反射的Ruby代码。

即便是在非反射性的Java代码中,往往也会丢失静态类型信息。譬如说,命令设计模式里面的 execute()方法在Java 5之前的代码里面返回的是"对象",而不是特定类型,结果导致ClassCastExceptions。同样,如果签名在编译时和运行时发生变化,运行时错误就会随之而来。实际上,无论是Java还是Ruby,这类错误很少会导致出现严重的字段错误。可靠的单元测试套件通常可以及时发现这些错误。

Ruby的动态类型意味着不必重复:如果使用Java,开发人员经常不得不需要为"XMLPersistence xmlPersistence = (XMLPersistence)persistenceManager.getPersistence();?"等行编写冗长的代码。而Ruby不需要类型声明和类型转换(也不需要圆括号和分号):典型的Ruby代码是"xmlPersistence = persistence_manager.persistence"。

Ruby的动态类型并不意味着弱类型――Ruby总是要求传递正确类型的对象。实际上,Java实施类型比Ruby还要弱。譬如说,Java将"4" + 2求值为"42",把整数和字符串强制起来,而Ruby会抛出"类型错误",告诉开发人员"无法把长整数转换成字符串"。同样,Java为了求速度而牺牲了正确性,它会悄悄导致整数运算溢出,从而带来莫名其妙的问题,譬如Integer.MAX_VALUE + 1,这相当于Integer.MIN_VALUE ,而Ruby只是在需要时才扩展整数。

尽管Ruby具有优点,但Java的静态类型的确为它提供了一项功能,使它成为大规模项目的优先选择:Java工具在开发时明白代码。集成开发环境(IDE)能够跟踪类与类的依赖关系、找到方法和类的使用、自动完成识别符、并且帮助开发人员重构代码。虽然也有类似的Ruby工具,但功能有限,而且缺乏类型信息,所以无法执行所有这些任务。



没有评论: