ruby中的对象引用类型

时间:2009-09-25 01:48:36

标签: java ruby variables dynamic-typing

我是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思考,不能动摇我的隧道视野。

非常感谢任何实际的解释。

4 个答案:

答案 0 :(得分:3)

Ruby中没有任何类型安全性。对Ruby而言,最重要的是对象是否可以响应它收到的消息。你可以传递一些完全没有意义的东西,Ruby也不会做任何事情来阻止你。但是如果传入的对象不响应您正在发送的消息(在这种情况下为+和 - ),当您的代码尝试发送无效消息时,您将收到NoMethodError。

一般来说,解决方法是:不要传入错误的类型。它知道它听起来很弱,但这几乎是你需要做的 - 确保你传递正确的东西。为您的程序编写测试,以确保您正在做您想做的事情。 Ruby在单元测试方面非常重要。如果您真的担心参数是正确的类型,您可以显式检查其类(raise 'WTF?' unless object.class == String),也可以尝试将其转换为正确的类(通过定义to_foo - 型方法)。

作为回报,你基本上不再关心类型。只要对象响应你发送的消息,你输入的内容就没关系。这使得模拟和代理之类的东西变得非常简单。

答案 1 :(得分:2)

它被称为"duck typing" - 我建议先阅读链接的维基百科文章,然后看看在此之后是否还有任何问题。

答案 2 :(得分:1)

实际上,所有现代语言都是type-safememory-safe,并不总是静态输入


这些都是好问题,而且它确实是动态与静态类型范式的核心。

碰巧,你担心的大部分都不太严重:

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