我是Ruby新手,目前正在尝试使用我正在使用的Ruby书中的几个例子:
class Account
attr_accessor :balance
def initialize(balance)
@balance = balance
end
end
class Transaction
def initialize(account_a, account_b)
@account_a = account_a
@account_b = account_b
end
def debit(account,amount)
account.balance -= amount
end
def credit(account,amount)
account.balance += amount
end
def transfer(amount)
debit(@account_a, amount)
credit(@account_b, amount)
end
end
savings = Account.new(100)
checking = Account.new(200)
trans = Transaction.new(checking, savings)
trans.transfer(60)
puts savings.balance
puts checking.balance
这是一个非常简单的示例,在同一个脚本文件中包含两个类。我对传递给信用卡和借记卡方法的论证类型感到困惑。来自Java,我仍然在类型方面,所以很明显我传递的帐户变量的类型,比如借记方法,必须是Account类型。
由于ruby是动态类型的,并且没有检查类型,我如何安全地操作我传递的参数,并通过说:account.balance - + amount?
来定义方法的其余部分。我试图理解,如果我向借记方法传递对除帐户以外的对象的引用,会有什么样的安全性?
当定义下面方法的主体时,它使用给定的参数帐户。现在,我想我正在重复自己,因为我仍然无法理解这个想法......我怎么能把参数帐户(可以是任何类型,因为没人检查)和通过使用点运算符构建一些逻辑,询问其实例变量,或调用其他方法,并对可能或可能不是正确类型(或类型)的对象执行计算?当然,隐含地,我希望它是Account类型。
def credit(account,amount)
account.balance += amount
end
另外,如果我在不同的文件中声明这两个类,那么同样的例子会如何工作?
对于新手问题提前道歉,我发现很难围绕动态打字 - 或者更好,没有类型检查。这本书要么有点模糊,要么我只能用java思考,不能动摇我的隧道视野。
非常感谢任何实际的解释。
答案 0 :(得分:3)
Ruby中没有任何类型安全性。对Ruby而言,最重要的是对象是否可以响应它收到的消息。你可以传递一些完全没有意义的东西,Ruby也不会做任何事情来阻止你。但是如果传入的对象不响应您正在发送的消息(在这种情况下为+和 - ),当您的代码尝试发送无效消息时,您将收到NoMethodError。
一般来说,解决方法是:不要传入错误的类型。它知道它听起来很弱,但这几乎是你需要做的 - 确保你传递正确的东西。为您的程序编写测试,以确保您正在做您想做的事情。 Ruby在单元测试方面非常重要。如果您真的担心参数是正确的类型,您可以显式检查其类(raise 'WTF?' unless object.class == String
),也可以尝试将其转换为正确的类(通过定义to_foo
- 型方法)。
作为回报,你基本上不再关心类型。只要对象响应你发送的消息,你输入的内容就没关系。这使得模拟和代理之类的东西变得非常简单。
答案 1 :(得分:2)
它被称为"duck typing" - 我建议先阅读链接的维基百科文章,然后看看在此之后是否还有任何问题。
答案 2 :(得分:1)
这些都是好问题,而且它确实是动态与静态类型范式的核心。
碰巧,你担心的大部分都不太严重:
Ruby是类型安全的,至少通过它自己的定义,它绝对是内存安全的。它确实检查每个操作的类型。 (排序,参数传递等一些操作没有要检查的类型限制。)
它确实有很多自动转换,但它仍然是类型安全的。
它确实有动态类型,但它仍然是类型安全的。
确实,方法参数没有“错误”类型。使用该参数时将进行检查。也没有必要检查你收到的类型...... Ruby会在你使用它时检查你。你只是在运行时而不是编译时得到你的错误,但你还是要进行单元测试,对吧? :-)查找“Duck typing”非常有用,因为兼容类型的定义在Ruby中比平常更广泛,但仍然有一个定义,并且会进行检查。
对于一个非常大的程序,传统观点认为像Java和Scala这样的静态类型语言是更安全的选择,因为它们可以充满信心地进行重构,并且您可以获得额外的编译时检查。然而,许多人对传统观念提出异议,因为它涉及将一系列复杂的权衡结果减少为1位结论。
答案 3 :(得分:1)
传递的对象,它的引用,乍一看可以被认为是未知的,但Ruby是强类型的;此外,它还具有使用“鸭子打字”的功能。我将举几个例子。
让我们使用财务账户的例子。
您可以使用case语句来确定对象的类型:
class Account
attr :balance # add ", true" to make it writeable
def initialize(balance)
case balance
when String: @balance = balance.to_f
when Fixnum, Float: @balance = balance
else
raise TypeError, "Can't initialize an account with a balance of type #{obj.class}."
end
end
end
另一个选项,当您进行一项测试时,对象的is_a?
测试,如obj.is_a?(Fixnum)
将返回true / false,判断它是否为整数。
您可以使用断言来强制执行类型:
class Account
attr :balance # add ", true" to make it writeable
def initialize(balance)
assert_eql balance.class, Float
@balance = balance
end
end
我建议您在Transaction类中使用if语句并引发异常:
class Transaction
def initialize(account_a, account_b)
raise ParameterError, "Both parameters must be accounts!" unless account_a.is_a?(Account) && account_b.is_a?(Account)
@account_a = account_a
@account_b = account_b
end
def transfer(amount)
debit(@account_a, amount)
credit(@account_b, amount)
end
private
def debit(account,amount)
account.balance -= amount
end
def credit(account,amount)
account.balance += amount
end
end