Ruby Method Visibility
In Java (and most OO languages), declaring a method as private means that the method can only be accessed within the context of the defining class. Similarly, protected means that the method can only be accessed from that class or any of its subclasses.
In Ruby, things are a bit different. Both private and protected methods can be called by any instance of the defining class and its subclasses. Inheritance plays absolutely no role in determining the visibility of a method. The difference instead is that private methods can never be called with an explicit receiver, even if the receiver is self. This means that it's not possible to access another object's private methods, even if the object is of the same type as the caller. A private method must be called from within the calling object.
Consider the following example:
class Person
def public_method
"public"
end
protected
def protected_method
"protected"
end
private
def private_method
"private"
end
end
class SalesPerson < Person
def check_protected_method_explicit_receiver
"#{self.protected_method} method OK with explicit receiver"
rescue
"failure accessing protected method with explicit receiver"
end
def check_protected_method_implicit_receiver
"#{protected_method} method OK with implicit receiver"
rescue
"failure accessing protected method with implicit receiver"
end
def check_private_method_explicit_receiver
"#{self.private_method} method OK with explicit receiver"
rescue
"failure accessing private method with explicit receiver"
end
def check_private_method_implicit_receiver
"#{private_method} method OK with implicit receiver"
rescue
"failure accessing private method with implicit receiver"
end
end
The public method can of course be accessed from anywhere (in this case, outside the class with an explicit receiver) and both private and protected methods of Person will obviously raise a NoMethodError.
Person.new.public_method
=> "public"
Person.new.private_method
=> NoMethodError: private method `private_method' called for #<Person:0x6ec698>
Person.new.protected_method
=> NoMethodError: protected method `protected_method' called for #<Person:0x6ea5c8>
So the protected method cannot be accessed outside of the class or it's subclass, but from within the subclass, using it with either an implicit or explicit receiver works fine:
SalesPerson.new.check_protected_method_explicit_receiver
=> "protected method OK with explicit receiver"
SalesPerson.new.check_protected_method_implicit_receiver
=> "protected method OK with implicit receiver"
The private method can also be called from the subclass, but note how it only works with an implicit receiver:
SalesPerson.new.check_private_method_explicit_receiver
=> "failure accessing private method with explicit receiver"
SalesPerson.new.check_private_method_implicit_receiver
=> "private method OK with implicit receiver"
This also means you can do stuff like:
class Person
def ==(other)
protected_method == other.protected_method
end
end
x = SalesPerson.new
y = Person.new
x == y
=> true
We're accessing the protected_method of another class instance that shares our type here, specifying an explicit receiver. If you were to try to use private_method instead, a NoMethodError would be raised. You could also just call other.send("private_method") if you really wanted to, violating our encapsulation and angering the gods of object orientation. This is only a "little sin" though, and can be permissible if the situation calls for it (like, cough cough, my latest commit to DataMapper).
In summary, method visibility and access control can be a bit confusing at first, especially if you're coming over to Ruby from some other OO language. If you're still confused, there's more information available here and here. Do yourself a favor and make sure you understand, cuz it's important stuff!
Comments
-
You might want to edit your code - both classes end a little early :)
-
Ugh looks like Textile took some liberties with the markup. Thanks for pointing it out.
-
Thank you for bring in this up. I think I need more study on this.