accessing protected methods in Ruby

2019-06-17 02:51发布

I am trying to work our for myself access modifiers in Ruby. I have:

class Person
  def initialize (first_name, last_name, age)
        @first_name=first_name
        @last_name=last_name
        @age=age
    end


    def show()
        puts @first_name
        puts @last_name
        puts @age
    end

protected
  def compare(other)
    self.instance_variable_get(:@age)<=>other.instance_variable_get(:@age)
  end

end

p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"

p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"

p1.compare(p2)

I am getting the error "protected method `compare' called for # (NoMethodError)" I have tried calling from within the class and without. I pasted the without version here. I thought that protected methods could be called on other objects of the same class. What does this error mean and how would I properly use a protected method here? Thank you for your help.

2条回答
淡お忘
2楼-- · 2019-06-17 03:25

You can call a protected method in a public method of the class...

class Person
  def initialize (first_name, last_name, age)
    @first_name=first_name
    @last_name=last_name
    @age=age
  end

  def same_age?(other)
    age == other.age
  end

  def show
    puts @first_name
    puts @last_name
    puts @age
  end

  protected

  def age
    @age
  end

end

p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"

p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"

# calls a method that calls a protected method
p1.same_age?(p2)
=> false

# but you can't call #age directly...
begin 
 p1.age
rescue NoMethodError
  puts "no method error (protected)"
end
查看更多
太酷不给撩
3楼-- · 2019-06-17 03:27

You got the wrong view of the protected visibility. The Ruby doc says:

The second visibility is protected. When calling a protected method the sender must be a subclass of the receiver or the receiver must be a subclass of the sender. Otherwise a NoMethodError will be raised.

So the restriction of the visibility is applied to the sender, not the receiver as what you thought.

If you want to call compare outside of the instance methods, you need to use public visibility. You need to remove the protected modifier if you can. This is the recommended way.

If the code is fixed and you cannot modify that piece of code, you can use the Object#send method. Object#send will bypass the visibility constraint and can access even private methods.

p1.send(:compare, p2)

Or you can reopen the class and change the visibility of the compare class:

# you code here

# reopen and modify visibility
class Person
  public :compare
end

p1.compare(p2)
查看更多
登录 后发表回答