新手问题 当 self.attr.nil? 时 self.attr ||= 和 self.attr = 的区别

bolasblack · 2014年10月29日 · 最后由 serco 回复于 2014年10月30日 · 2527 次阅读

我是一个初学者,昨天在写代码时遇到了一个奇异的情况

示例代码:

class A
  attr_accessor :a
  private :a=
  def initialize
    self.a ||= 1
  end
end

然后当我实例化这个模型时,给我报错说:

NoMethodError: private method `a=' called for #<A:0x007fbbab4dbbb0>

如果代码改成这样就不会报错了

self.a || self.a = 1
# 或者这样子也没问题
self.a = self.a || 1

请问这是一个 BUG 还是本来就是这么设计的?如果本来就是这么设计的……为什么要这么设计啊…………………………

http://stackoverflow.com/a/9882795

What is the difference between 'private' and 'protected' methods in Ruby? In Ruby, the primary difference between a 'private' and 'protected' method is that a private method cannot be called with an explicit receiver, while a protected method can. What is an 'explicit receiver', you ask? An explicit receiver is the object that is receiving a message. In the following example, we have a receiver ('parent') and a method ('get_name'). The 'parent' object is receiving the instruction to perform the 'get_name' method.

private method 调用时是不允许明确的使用对象的,即使是 self

class Foo
  def b
    self.a
  end

  def c
    a
  end

private
  def a
    puts 'a invoked'
  end
end

f = Foo.new

f.a
f.b
f.c

# edit private -> protected, and try again

self.address= 和 self.timezone= 都被我重新定义过并且设置为 private 一般不会有人这么干

如果不想暴漏修改属性的接口

  • 如一楼 protected,类内 self.address= 没问题
  • 直接修改实例变量(如果你用了的话)

#2 楼 @davidqhr 难道 initialize 里的代码不是在类内的吗?为什么在 initialize 里使用 self.a ||= 1 也会报错?

自己顶一下,找了不少资料似乎都没有这方面的说法,求大家为我解释一下……

#1 楼 @orzfly #2 楼 @davidqhr private 不能显示调用是有一个特例的的,那就是 setter 方法 self.name = 1 如果写成 name =1,那么 name 会被当成 局部变量的

#4 楼 @bolasblack 你遇到的问题,其实是 Ruby 的 bug https://bugs.ruby-lang.org/issues/10060 https://bugs.ruby-lang.org/issues/9907

可能会在之后的版本修正吧,关注一下这两个帖子好了

另外,你可以考虑直接修改实例变量而不是要一个 private 的 attr_writer

#5 楼 @serco 其实不是什么 bug 错就错在 private 的 attr_writer

#5 楼 @serco 原来真的是这样子……太谢谢你了……

#6 楼 @davidqhr 必须是 bug,不然为什么同样 name= 是 private method 的情况下, self.name = 'abc' 可以, self.name ||= 'abc' 不行,行为不一致啊。而且 Nobuyoshi Nakada 都说是了。

其实 private attr_writer 也并不都错,不是所有场景都可以用实例变量来代替的

需要 登录 后方可回复, 如果你还没有账号请 注册新账号