Menu
CoddyTech

方法中的 self

本课属于 Coddy Ruby 学习旅程的“面向对象编程”单元。第 9 课,共 57 课。

在方法内部,name = value 总是会创建一个局部变量,即使类中有一个 setter name= 也是如此。要调用 setter,请写上接收者:self.name = value:

class Player
  attr_accessor :level

  def initialize
    @level = 1
  end

  def broken_level_up
    level = level + 1
  rescue NoMethodError
    puts "level is a new local variable here, still nil"
  end

  def level_up
    self.level = level + 1
  end
end

p1 = Player.new
p1.broken_level_up
p1.level_up
puts p1.level

输出:

level is a new local variable here, still nil
2

读取器不需要 self.:level 和 self.level 会调用同一个方法。通常只在 Ruby 需要 self. 时使用它,也就是在 setter 前面:

class Rect
  attr_accessor :w, :h

  def initialize(w, h)
    @w = w
    @h = h
  end

  def area
    w * h
  end

  def double!
    self.w = w * 2
    self.h = self.h * 2
    self
  end
end

puts Rect.new(2, 3).double!.area

输出:

24

从 initialize 中使用 self. 调用 setter,可以让新对象和之后的修改通过相同的检查,这样规则就只需写在一个地方:

class Product
  attr_reader :price

  def initialize(price)
    self.price = price
  end

  def price=(value)
    raise ArgumentError, "price must be positive" if value <= 0

    @price = value
  end
end

puts Product.new(5).price
begin
  Product.new(0)
rescue ArgumentError => e
  puts e.message
end

输出:

5
price must be positive

self 的含义取决于代码运行的位置。在实例方法内部,它表示实例;在类体中、所有方法之外,它表示类本身:

class Robot
  puts "in the class body, self is #{self}"

  def whoami
    "in a method, self is a #{self.class}"
  end
end

puts Robot.new.whoami

输出:

in the class body, self is Robot
in a method, self is a Robot
challenge icon

挑战

简单

Account 类有一个自定义 setter balance=,当值小于 0 时会通过 balance cannot be negative 引发 ArgumentError。请完成 initialize(owner, balance)、deposit(amount) 和 withdraw(amount),确保每次修改余额都通过这个 setter,包括初始余额。

提供的代码会读取所有者和初始余额,然后读取 +n(存款)或 -n(取款)命令。每条命令执行后,代码会打印 Ada: 120;如果发生错误,则会打印错误消息和未改变的余额。初始余额无效时,只打印错误消息。

请将代码写在 account.rb 中。main.rb 包含提供的输入和输出代码,不能编辑。

亲手试试

require_relative 'account'

# 已提供的输入输出代码:保持原样。
input = []
while (line = gets)
  input << line.chomp
end
begin
  account = Account.new(input[0], input[1].to_i)
  input.drop(2).each do |cmd|
    begin
      amount = cmd[1..].to_i
      cmd.start_with?("+") ? account.deposit(amount) : account.withdraw(amount)
    rescue ArgumentError => e
      puts e.message
    end
    puts "#{account.owner}: #{account.balance}"
  end
rescue ArgumentError => e
  puts e.message
end
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Ruby 在线编译器