您可能会发现阅读 ruby 对公共、私有和受保护的定义很有帮助。(跳至访问控制)
Ruby 的 private 类似于 Java 的 protected。没有 Ruby 等价于 Java 的私有。编辑:这个解决方案现在提供了一种在 Ruby 对象中伪造 Java 理想私有的方法。
Private 被定义为只能被隐式调用的方法/变量。这就是语句 2 和 3 失败的原因。换句话说,私有将方法/变量限制在定义它们的类或子类的上下文中。继承将私有方法传递给子类,因此可以使用隐式 self 进行访问。(解释为什么陈述 6 有效。)
我认为您正在寻找更接近受保护的东西。它的行为类似于没有可见性的 Java 访问器(例如:公共、私有、受保护)通过将 Spy 中的私有更改为保护所有 6 个语句工作。受保护的方法可以由定义类或其子类的任何实例调用。只要调用者是响应调用的对象的类,或者从它继承,对 self 显式或隐式调用都是受保护方法的有效语句。
class Person
private
attr_reader :weight
end
class Spy < Person
protected
attr_accessor :code
public
def test
code #(1) OK: you can call a private method in self
Spy.new.code #(2) OK: Calling protected method on another instance from same class family or a descendant.
self.code #(3) OK: Calling protected method on with explicit self is allowed with protected
code="xyz" #(4) Ok, it runs, but it actually creates a local variable!!!
self.code="z" #(5) OK! This is the only case where explicit 'self' is ok
weight #(6) OK! You can call a private method defined in a base class
end
end
s = Spy.new
s.test # succeeds
s.code #(7) Error: Calling protected method outside of the class or its descendants.
至于陈述4。您假设这是为了避免歧义是正确的。它更多地是对 ruby 动态特性的潜在危害的保护。它确保您以后不能通过再次打开该类来覆盖访问器。可能出现的情况,例如通过评估受污染的代码。
我只能推测导致这些行为的设计决策。对于大多数情况,我觉得这归结为语言的动态特性。
PS如果你真的想给事物私有的java定义。仅可用于定义它的类,甚至不能用于子类。您可以向您的类添加一个 self.inherited 方法,以删除对您想要限制访问的方法的引用。
使子类无法访问 weight 属性:
class Person
private
attr_reader :weight
def initialize
@weight = 5
end
def self.inherited(subclass)
subclass.send :undef_method, :weight
end
end
class Spy < Person
private
attr_accessor :code
public
def test
weight
end
end
Person.new.send(:weight) # => 5
Spy.new.send(:weight) #=> Unhelpful undefined method error
将 undef_method 调用替换为如下内容可能更有意义:
def self.inherited(subclass)
subclass.class_eval %{
def weight
raise "Private method called from subclass. Access Denied"
end
}
end
它提供了更有用的错误和相同的功能。
发送对于绕过调用其他类的私有方法是必要的。仅用于证明事情确实有效。
事后看来,这使得私有和受保护无用。如果你真的很想保护你的方法,你将不得不重写 send 来阻止它们。以下代码基于对象的 private_methods 执行此操作:
def send_that_blocks_private_methods(method, *args)
if private_methods.include?(method.to_s)
raise "Private method #{method} cannot called be called with send."
else
send_that_allows_private_methods(method, *args)
end
end
alias_method :send_that_allows_private_methods, :send
alias_method :send, :send_that_blocks_private_methods
private :send_that_allows_private_methods
您可以指定要阻止访问而不是拒绝访问所有私有方法的 private_methods 的 class_variable。您也可以将 send 设为私有,但从对象外部调用 send 有合法用途。