ruby 为什么这段代码适用于受保护的,而不是私有的?

ktca8awb  于 2022-12-12  发布在  Ruby
关注(0)|答案(2)|浏览(145)

我对private和protected这两个词感到困惑。我读到过,在类之外创建的对象上调用private和protected方法是不可能的。我可以在public方法中使用它们。那么,为什么这段代码可以使用protected,而不是private呢?

class Student
  def initialize(name, grade)
    @name = name
    @grade = grade
  end

  def better_grade_than?(other_student)
    grade > other_student.grade ? true : false
  end

  protected
  def grade
    @grade
  end

end

class Joe < Student
end

class Bob < Student
end

joe = Joe.new('Joe', 88)
bob = Bob.new('Bob', 60)

puts joe.better_grade_than?(bob) # true
puts bob.better_grade_than?(joe) # false

如果是private,则会输出NoMethodError。

lc8prwob

lc8prwob1#

在Ruby中,protected方法可以被定义它的类以及继承它的类访问。另一方面,private方法只能被定义它的类访问。
在你的例子中,protected可以工作,因为Joe和Bob都继承了Student,所以他们可以使用它的保护方法。

gfttwv5a

gfttwv5a2#

我读到过在类外部创建的对象上调用private和protected方法是不可能的。
这是不对的。

第一个月

private意味着“只能由消息发送调用,该消息发送具有self的隐式接收者或具有作为字面伪变量关键字self的显式接收者”。
换句话说,private方法qux只能通过消息发送调用,如下所示:

qux      # implicit receiver

或类似于:

self.qux # `self` keyword as the explicit receiver

它 * 不能 * 与 * 任何其他接收器 * 一起调用 。即使self作为接收器也不行, 除非 * 它是实际的、字面的伪变量关键字self

this = self
self.qux
# private method `qux' called (NoMethodError)
#
#     this.qux
#         ^^^^

你可以自己试试:

class C
  def foo = qux
  def bar = self.qux

  def baz
    this = self
    this.qux
  end

  private def qux; end
end

o = C.new

o.foo
o.bar
o.baz

我不知道你是从哪里读到你引用的声明的,但我建议你总是阅读官方来源。
这就是13.3.5.3ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification的www.example.com Private methods 部分所要说明的内容,例如:

13.3.5.3私有方法

私有方法是其可见性属性设置为私有可见性的方法。
私有方法不能用显式接收器调用,即,如果 primary-expressionchained-method-invocation 出现在与方法调用中的方法接收器对应的位置,则私有方法不能被调用,但以下任何形式的方法调用除外,其中 primary-expressionself-expression

    • 单一方法赋值 *
    • 简化方法赋值 *
    • 单一索引指派 *
    • 缩写索引赋值 *

请注意,ISO Ruby编程语言规范自2012年以来一直没有更新,所以这是一个稍微过时的定义。该规范在几年前稍微简化了一下。现在,第二段可能应该读起来更像这样:
私有方法不能用显式接收器调用,即,如果 primary-expressionchained-method-invocation 出现在对应于方法调用中的方法接收器的位置,则私有方法不能被调用,但 primary-expressionself-expression 的方法调用除外。
但请注意,这在这里没有区别,因为在代码中,无论如何都不会使用 * 自我表达 * 作为接收器,所以 * 自我表达 * 是在有限数量的情况下允许还是始终允许是无关紧要的。
privatemessage sendsruby/spec似乎也不是完全最新的,但同样,这些差异对您的问题并不重要。
然而,Modules的RDoc,特别是关于可见性的小节,是完全最新的,而且非常清楚:
第三个可见性是private。private方法只能从拥有者类别内部呼叫,而不需要接收者,或以常值self做为接收者。如果呼叫private方法时使用的接收者不是常值self,则会引发NoMethodError

protected

protected表示“只能由从发送方发送的消息调用,该发送方是定义该方法的模块的示例”。
换句话说,如果在模块M中定义了protected方法m,如下所示:

module M
  protected def m; end
end

m只能由消息send调用,其中发送方是M的示例:

kind_of?(M) #=> true
M === self  #=> true

receiver.m

我 * 认为 * 一个简洁的方式来表达测试将是这样的:

kind_of?(receiver.method(:m).owner) # must be `true`

以下是13.3.5.4ISO/IEC 30170:2012 * 信息技术-编程语言- Ruby* 规范的www.example.com * 受保护的方法 * 部分的内容:

13.3.5.4受保护方法

受保护的方法是其可见性属性设置为受保护的可见性的方法。
当且仅当满足以下条件时,才可以调用受保护的方法:

  • M 为类Module的示例,其中存在该方法的绑定。
  • M* 包含在当前self中,或者 M 是当前self的类或其超类之一。

如果 M 是单例类,则可以通过实现定义的方式来确定该方法是否可以被调用。
以及RDoc:
第二个可见性是protected。调用受保护的方法时,发送方必须继承定义该方法的ClassModule。否则将引发NoMethodError
受保护的可见性最常用于定义==和其他比较方法,在这些方法中,作者不希望将对象的状态公开给任何调用方,而希望将其仅限制为继承的类。
那么,为什么这段代码适用于protected而不是private呢?
它不适用于private,因为这里发送的消息的显式接收者不是文本伪变量关键字self

grade > other_student.grade ? true : false
#       ↑↑↑↑↑↑↑↑↑↑↑↑↑

接收方为other_student,但如果gradeprivate,则只能由消息发送调用,消息发送使用隐式接收方(grade)或显式接收方selfself.grade)。
因为other_studentStudent的一个示例,所以它 * 确实 * 适用于protected

相关问题