问题
If a method is protected, it may be called by any instance of the defining class or its subclasses. If a method is private, it may be called only within the context of the calling object—it is never possible to access another object’s private methods directly, even if the object is of the same class as the caller.
This definition i got from net when searching for the difference between private and protected methods in ruby.
I have 2 doubts in this
class Abc
def abc
xyz
end
protected
def xyz
p "hai"
end
end
a=Abc.new
a.abc
In this i am calling xyz implicitly on object a, this is how i will call xyz even if it is private.So "it may be called by any instance of the defining class" what does this mean ?????
Secondly,
class Abc
def abc(obj)
obj.xyz1
obj.xyz2
obj.xyz3
end
end
class Xyz
def xyz1
p "called"
end
private
def xyz2
p "called"
end
protected
def xyz3
p "called"
end
end
a=Abc.new
b=Xyz.new
a.abc(b)
In this i can call obj b's xyz1 method on object a.But i cant call obj b's protected xyz3 and private method xyz2 method on object a.So "it is never possible to access another object’s private methods directly, even if the object is of the same class as the caller." what does this exact mean???
can anybody help me to understand this one with better examples????
回答1:
Private Methods
To define a private method, we use the private keyword, which is actually a built-in method implemented in a class called Module. A private method can only be called by another method within the class on which it was defined (or one of its subclasses).
class Koan
def call_say_koan
say_koan
end
private
def say_koan
puts "What is the sound of one hand clapping?"
end
end
k = Koan.new
k.say_koan # Output: NoMethodError: private method `say_koan' called for #<Koan:0x000000021e7380>
k.call_say_koan # Output: What is the sound of one hand clapping?
In the above example, we could not call the say_koan private method directly (from outside the class), but we could call the call_say_koan public method which, in turn, called say_koan.
Also in the above example, the built-in private method was used with no arguments. Hence, all methods defined below it were made private.
The private method can also be used with previously defined method names (passed as symbols) as arguments.
class Foo
def some_method
end
private :some_method
end
In order to make a class method private, use the private_class_method keyword/method instead of private.
Private methods can't be called with a receiver, such as self. Trying to call the say_koan method with self as a receiver (self.say_koan) within call_say_koan would result in the following exception:
NoMethodError: private method `say_koan' called for #<Koan:0x000000021eb548>
As of Ruby 2.0, the respond_to? method will return false when given a private method as an argument.
k.respond_to? :say_koan # Output: => false
To list all private instance methods in a class, use the private_instance_methods built-in method. For private class methods, use private_methods.
Koan.private_instance_methods(false) # Output => [:say_koan]
Protected Methods
To define a protected method, we use the protected keyword (which is actually a method). Like private methods, protected methods can also be called by other methods within the class on which it was defined (or one of its subclasses). The difference is, protected methods can also be called from within other instances of the same class.
There is no such thing as a protected a class method, Ruby only supports protected instance methods.
Let's suppose we need to select a few meditators to participate in a study. To find the most experienced meditators, we need to compare their total hours of meditation. However, we don't want the number of hours to be visible.
class Meditator
def initialize(hours)
@hours = hours
end
def more_experienced?(other_person)
hours > other_person.hours
end
protected
attr_reader :hours # We have made the accessor protected
end
m1 = Meditator.new 3000
m2 = Meditator.new 5000
m2.more_experienced? m1 # Output: => true
m1.more_experienced? m2 # Output: => false
Similar code could be used to protect any kind of sensitive data from outside access (outside the class and its instances), although protected methods are not commonly employed in Ruby.
When called with no arguments (as in the above example), the protected method turns all methods defined below it into protected methods. It can also be used to protect previously defined methods, as in the following example.
class Foo
def some_method
end
protected :some_method
end
To list all protected instance methods in a class, use the protected_instance_methods built-in method. For protected class methods, use protected_methods.
Meditator.protected_instance_methods(false) # Output: => [:hours]
回答2:
The best way to think of protected and private methods is how they affect whether you can explicitly specify a receiver in front of a method call, i.e. think of it as a technical rule, rather than some meta idea.
private method: You cannot explicitly specify a receiver in front of a private method call.
Ever(see comment). Because ruby always calls a method with some receiver, ruby uses whatever object is currently assigned to theself
variable as the receiver.protected method: In some situations, you can explicitly specify a receiver for a protected method call.
Then it's just a matter of exploring when ruby lets you explicitly specify a receiver in front of a protected method call:
class Dog
def execute_pro(dog)
dog.pro_meth
end
protected
def pro_meth
puts 'protected'
end
end
d1 = Dog.new
p d1.protected_methods
d1.pro_meth
--output:--
[:pro_meth]
1.rb:15:in `<main>': protected method `pro_meth' called for #<Dog:0x007f8ef90e0eb8> (NoMethodError)
The example above demonstrates that the statement:
If a method is protected, it may be called by any instance of the defining class or its subclasses.
is too general. But:
class Dog
def execute_pro(dog)
dog.pro_meth
end
protected
def pro_meth
puts 'protected'
end
end
d1 = Dog.new
d2 = Dog.new
p d1.protected_methods
d1.execute_pro d2
--output:--
[:pro_meth]
protected
it is never possible to access another object’s private methods directly, even if the object is of the same class as the caller.
In the example above, the caller is d1, and inside of methods called by d1 you are able to access d2's protected methods directly, i.e. you can explicitly specify a receiver in front of a protected method call, because d2 is of the same class as d1. You would not be able to directly access d2's private methods there--because you can NEVER can't explicitly specify a receiver in front of a private method call. That might lead you to believe..."Okay, private and protected are the same thing: I just need to drop the explicit receiver if the method is private:
class Dog
def execute_pro(dog)
pro_meth #<*****CHANGE HERE
end
private #<****CHANGE HERE
def pro_meth
puts 'protected'
end
end
d1 = Dog.new
d2 = Dog.new
d1.execute_pro d2
--output:--
protected
The output is the same...but the conclusion, "Private and protected are the same thing: I just need to drop the explicit receiver if the method is private." is wrong. The example is too simple and it masks a subtle difference between private and protected. Here's a better example:
class Dog1
def initialize(num)
@num = num
end
def execute_pro(dog)
dog.pro_meth
end
protected
def pro_meth
puts "protected -> #{@num}"
end
end
d1 = Dog1.new 1
d2 = Dog1.new 2
d1.execute_pro d2
class Dog2
def initialize(num)
@num = num
end
def execute_pro(dog)
pro_meth #<****CHANGE HERE
end
private #<******CHANGE HERE
def pro_meth
puts "protected -> #{@num}"
end
end
d1 = Dog2.new 1
d2 = Dog2.new 2
d1.execute_pro d2
--output:--
protected -> 2
protected -> 1
The output shows that you cannot just convert a protected method to a private method and remove the explicit receiver from the method calls and always get the same results. In this case, ruby lets you explicitly specify a receiver in front of a protected method call in order to let you direct the method call to the proper object. But a protected method is not the same as a public method:
class Dog
def execute_pro(dog)
dog.pro_meth
end
protected
def pro_meth
puts 'protected'
end
end
class Poodle < Dog
def dostuff(dog)
dog.pro_meth
end
end
class Cat
def dostuff(dog)
dog.pro_meth
end
end
d1 = Dog.new
p1 = Poodle.new
c1 = Cat.new
p1.dostuff d1
c1.dostuff d1
--output:--
protected
1.rb:21:in `dostuff': protected method `pro_meth' called for #<Dog:0x007fe9040d28f8> (NoMethodError)
from 1.rb:30:in `<main>'
In the line c1.dostuff d1
, the caller is c1, and inside methods called by c1, you cannot call Dog's protected methods--because the Cat class is not the same class or a subclass of the Dog class. Also note that if you tried removing the explicit receiver:
class Cat
def dostuff(dog)
pro_meth #<****CHANGE HERE
end
end
that won't call Dog's protected method either, because inside of dostuff(), ruby assigns the caller to the self variable, and the caller is c1, so you get:
class Cat
def dostuff(dog)
#self = c1
pro_meth
end
end
...
c1.dostuff d1
and ruby converts the method call pro_meth
into self.pro_meth
, which is equivalent to c1.pro_meth
, and the Cat class does not define a method named pro_meth
.
回答3:
The examples you picked somewhere are really confusing (especially the names being either abc or xyz, with very little context). Please allow me to use a different example.
The theories are like this :
– Both Private and Protected be accessed from outside the class through a public method.
The differences between Protected and Private are :
– Private method can not be called with a receiver (not even with #self). Unless … calling a Private setter method. If you try to remove the receiver, Ruby will create a local variable. Self is a must in this case.
– Protected may or may not use a receiver such as #self.
– Protected can access another object’s protected method that comes from the same class, Private can't. (see code for method #eat_more_than(other))
Now when it comes to Inheritance …
– Private methods can only be called on subclasses implicitly (simply just the name of the method) but not explicitly (using #self).
– Protected can be called both ways (with or without #self || implicitly or explicitly).
Let's take a look on the example below :
class Dog
attr_accessor :name, :age
def initialize(n, a)
self.name = n
self.age = a
end
def accessing_private
"#{self.name} in human years is #{human_years}. This is secret!"
end
def accessing_protected
"Will this work? " + a_protected_method
end
def eat_more_than(other)
# accessing other instance's protected method from the same class
daily_diet < other.daily_diet
"#{name} eats more than #{other.name}"
end
def boy
gender_method("boy") # accessing private setter method
end
protected
def daily_diet
age * 2 # the younger, the more they have to eat
end
def a_protected_method
"Yes, I'm protected!"
end
private
attr_writer :gender
def gender_method(gender)
self.gender = gender # private setter method requires self
"#{name} is a #{gender}"
end
def human_years
age * 8
end
end
# Create the first object of Dog
blake = Dog.new("Blake", 5)
p blake.accessing_private # "Blake in human years is 16. This is secret!"
p blake.accessing_protected # "Will this work? Yes, I'm protected!"
# Create the second object of Dog
jackson = Dog.new("Jackson", 1)
# Below, protected methods from different objects of the same type/class
# are proven to share access
p jackson.eat_more_than(blake) # true -> "Jackson eats more than Blake"
# Below, accessing private setter method through a public method.
p blake.boy # Blake is a boy
Now on a subclass, you can not call an inherited private method with a receiver, but protected will do just fine either way (with or without a receiver) :
class Puppy < Dog
def accessing_protected_explicitly
"Explicitly calls '#{self.a_protected_method}'"
end
def accessing_protected_implicitly
"Implicitly calls '#{a_protected_method}'"
end
def accessing_private_implicitly
"#{self.name} is #{human_years} years old in human years. This is a secret!" # implicit
end
def accessing_private_explicitly
"#{self.name} is #{self.human_years} years old in human years" # explicit -> error
end
end
# Below, testing them on a subclass
booboo = Puppy.new("Booboo", 1 )
p booboo.accessing_protected_explicitly # works
p booboo.accessing_protected_implicitly # works
p booboo.accessing_private_implicitly # works
p booboo.accessing_private_explicitly # error, called on a receiver
I hope that helps!
来源:https://stackoverflow.com/questions/37952489/private-vs-protected-method-in-ruby