动态类型
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工具,但功能有限,而且缺乏类型信息,所以无法执行所有这些任务。
没有评论:
发表评论