144

在阅读本文之前,我认为 Ruby 中的访问控制是这样工作的:

  • public- 可以被任何对象访问(例如Obj.new.public_method
  • protected- 只能从对象本身以及任何子类中访问
  • private- 与受保护的相同,但该方法在子类中不存在

然而,看起来protectedprivate行为相同,除了您不能private使用显式接收器调用方法(即self.protected_method有效,但self.private_method无效)这一事实。

这有什么意义?什么情况下您不希望使用显式接收器调用您的方法?

4

8 回答 8

171

protected定义类或其子类的任何实例都可以调用方法。

private方法只能从调用对象中调用。您不能直接访问另一个实例的私有方法。

这是一个快速的实际示例:

def compare_to(x)
 self.some_method <=> x.some_method
end

some_method不能在private这里。一定是protected因为您需要它来支持显式接收器。您的典型内部辅助方法通常可以是private因为它们永远不需要像这样调用。

需要注意的是,这与 Java 或 C++ 的工作方式不同。privateRuby中的子类与protectedJava/C++ 中的类似,因为子类可以访问该方法。private在 Ruby 中,无法像在 Java 中那样限制从其子类访问方法。

无论如何,Ruby 中的可见性在很大程度上是一种“建议”,因为您始终可以使用以下方法访问方法send

irb(main):001:0> class A
irb(main):002:1>   private
irb(main):003:1>   def not_so_private_method
irb(main):004:2>     puts "Hello World"
irb(main):005:2>   end
irb(main):006:1> end
=> nil

irb(main):007:0> foo = A.new
=> #<A:0x31688f>

irb(main):009:0> foo.send :not_so_private_method
Hello World
=> nil
于 2010-08-20T20:20:32.187 回答
79

区别

  • 任何人都可以调用您的公共方法。
  • 您可以调用受保护的方法,或者您的类(或后代类)的其他成员可以从外部调用您的受保护方法。没有其他人可以。
  • 只有你可以调用你的私有方法,因为它们只能用self. 即使你不能打电话self.some_private_method;你必须private_methodself暗示跟注。
    • iGEL指出:“但是有一个例外。如果你有一个私有方法 age=,你可以(并且必须)用 self 调用它,以将它与局部变量分开。”
    • 由于Ruby 2.7接收self器可以是显式的,self.some_private_method因此是允许的。(任何其他显式接收器仍然不允许,即使运行时值与 相同self。)

在 Ruby 中,这些区别只是一个程序员给另一个程序员的建议。非公开方法是一种表达“我保留改变它的权利;不要依赖它”的方式。但是你仍然可以得到锋利的剪刀send并且可以调用任何你喜欢的方法。

一个简短的教程

# dwarf.rb
class Dwarf
  include Comparable

  def initialize(name, age, beard_strength)
    @name           = name
    @age            = age
    @beard_strength = beard_strength
  end

  attr_reader :name, :age, :beard_strength
  public    :name
  private   :age
  protected :beard_strength

  # Comparable module will use this comparison method for >, <, ==, etc.
  def <=>(other_dwarf)
    # One dwarf is allowed to call this method on another
    beard_strength <=> other_dwarf.beard_strength
  end

  def greet
    "Lo, I am #{name}, and have mined these #{age} years.\
       My beard is #{beard_strength} strong!"
  end

  def blurt
    # Not allowed to do this: private methods can't have an explicit receiver
    "My age is #{self.age}!"
  end
end

require 'irb'; IRB.start

然后你可以运行ruby dwarf.rb并执行此操作:

gloin = Dwarf.new('Gloin', 253, 7)
gimli = Dwarf.new('Gimli', 62,  9)

gloin > gimli         # false
gimli > gloin         # true

gimli.name            # 'Gimli'
gimli.age             # NoMethodError: private method `age'
                         called for #<Dwarf:0x007ff552140128>

gimli.beard_strength # NoMethodError: protected method `beard_strength'
                        called for #<Dwarf:0x007ff552140128>

gimli.greet          # "Lo, I am Gimli, and have mined these 62 years.\
                           My beard is 9 strong!"

gimli.blurt          # private method `age' called for #<Dwarf:0x007ff552140128>
于 2013-03-18T14:39:52.157 回答
53

Ruby 中的私有方法:

如果一个方法在 Ruby 中是私有的,那么它就不能被显式的接收者(对象)调用。它只能被隐式调用。它可以被描述它的类以及这个类的子类隐式调用。

下面的例子将更好地说明它:

1) 具有私有方法 class_name 的 Animal 类

class Animal
  def intro_animal
    class_name
  end
  private
  def class_name
    "I am a #{self.class}"
  end
end

在这种情况下:

n = Animal.new
n.intro_animal #=>I am a Animal
n.class_name #=>error: private method `class_name' called

2) Animal 的一个子类,称为 Amphibian:

class Amphibian < Animal
  def intro_amphibian
    class_name
  end 
end 

在这种情况下:

  n= Amphibian.new
  n.intro_amphibian #=>I am a Amphibian
  n.class_name #=>error: private method `class_name' called

如您所见,私有方法只能被隐式调用。它们不能被显式接收者调用。出于同样的原因,不能在定义类的层次结构之外调用私有方法。

Ruby 中的受保护方法:

如果一个方法在 Ruby 中是受保护的,那么它可以被定义类及其子类隐式调用。此外,它们也可以由显式接收者调用,只要接收者是自己或与自己属于同一类:

1) 具有受保护方法protect_me 的Animal 类

class Animal
  def animal_call
    protect_me
  end
  protected
  def protect_me
    p "protect_me called from #{self.class}"
  end  
end

在这种情况下:

n= Animal.new
n.animal_call #=> protect_me called from Animal
n.protect_me #=>error: protected method `protect_me' called

2)从动物类继承的哺乳动物类

class Mammal < Animal
  def mammal_call
    protect_me
  end
end 

在这种情况下

n= Mammal.new
n.mammal_call #=> protect_me called from Mammal

3)从Animal类继承的两栖类(与哺乳动物类相同)

class Amphibian < Animal
  def amphi_call
    Mammal.new.protect_me #Receiver same as self
    self.protect_me  #Receiver is self
  end   
end

在这种情况下

n= Amphibian.new
n.amphi_call #=> protect_me called from Mammal
             #=> protect_me called from Amphibian  

4) 一个名为 Tree 的类

class Tree
  def tree_call
    Mammal.new.protect_me #Receiver is not same as self
  end
end

在这种情况下:

n= Tree.new
n.tree_call #=>error: protected method `protect_me' called for #<Mammal:0x13410c0>
于 2015-01-17T21:50:18.063 回答
7

考虑 Java 中的私有方法。当然,它可以在同一个类中调用,但也可以由同一个类的另一个实例调用:

public class Foo {

   private void myPrivateMethod() {
     //stuff
   }

   private void anotherMethod() {
       myPrivateMethod(); //calls on self, no explicit receiver
       Foo foo = new Foo();
       foo.myPrivateMethod(); //this works
   }
}

所以——如果调用者是我同一个类的不同实例——我的私有方法实际上可以从“外部”访问,可以这么说。这实际上使它看起来不是那么私密。

另一方面,在 Ruby 中,私有方法实际上只对当前实例是私有的。这就是删除显式接收器选项所提供的。

另一方面,我当然应该指出,在 Ruby 社区中,根本不使用这些可见性控件是很常见的,因为无论如何 Ruby 都为您提供了绕过它们的方法。与 Java 世界不同的是,它的趋势是让一切都可以访问并相信其他开发人员不会搞砸事情。

于 2010-08-20T20:27:23.943 回答
4

在 Ruby 中,子类可以访问私有方法的部分原因是 Ruby 与类的继承是对模块包含的薄糖衣——在 Ruby 中,类实际上是一种提供继承等的模块。

http://ruby-doc.org/core-2.0.0/Class.html

这意味着基本上子类“包含”父类,以便有效地在子类中定义父类的函数,包括私有函数

在其他编程语言中,调用方法涉及将方法名称冒泡到父类层次结构中并找到响应该方法的第一个父类。相比之下,在 Ruby 中,虽然父类层次结构仍然存在,但父类的方法直接包含在子类定义的方法列表中。

于 2015-02-21T01:27:12.970 回答
3

Java 与 Ruby 的访问控制比较:如果方法在 Java 中声明为私有,则它只能被同一类中的其他方法访问。如果一个方法被声明为受保护的,则它可以被同一个包中存在的其他类以及不同包中该类的子类访问。当一个方法是公开的时,它对每个人都是可见的。在 Java 中,访问控制可见性概念取决于这些类在继承/包层次结构中的位置。

而在 Ruby 中,继承层次结构或包/模块不适合。关键在于哪个对象是方法的接收者。

对于 Ruby 中的私有方法,它永远不能用显式接收器调用。我们可以(仅)使用隐式接收器调用私有方法。

这也意味着我们可以从声明它的类以及该类的所有子类中调用私有方法。

class Test1
  def main_method
    method_private
  end

  private
  def method_private
    puts "Inside methodPrivate for #{self.class}"
  end
end

class Test2 < Test1
  def main_method
    method_private
  end
end

Test1.new.main_method
Test2.new.main_method

Inside methodPrivate for Test1
Inside methodPrivate for Test2

class Test3 < Test1
  def main_method
    self.method_private #We were trying to call a private method with an explicit receiver and if called in the same class with self would fail.
  end
end

Test1.new.main_method
This will throw NoMethodError

您永远不能从定义它的类层次结构之外调用私有方法。

可以使用隐式接收器调用受保护的方法,就像私有方法一样。此外,如果接收者是“自己”或“同一类的对象”,也可以由显式接收者(仅)调用受保护的方法。

 class Test1
  def main_method
    method_protected
  end

  protected
  def method_protected
    puts "InSide method_protected for #{self.class}"
  end
end

class Test2 < Test1
  def main_method
    method_protected # called by implicit receiver
  end
end

class Test3 < Test1
  def main_method
    self.method_protected # called by explicit receiver "an object of the same class"
  end
end


InSide method_protected for Test1
InSide method_protected for Test2
InSide method_protected for Test3


class Test4 < Test1
  def main_method
    Test2.new.method_protected # "Test2.new is the same type of object as self"
  end
end

Test4.new.main_method

class Test5
  def main_method
    Test2.new.method_protected
  end
end

Test5.new.main_method
This would fail as object Test5 is not subclass of Test1
Consider Public methods with maximum visibility

概括

Public:公共方法具有最大的可见性

受保护的:受保护的方法可以使用隐式接收器调用,就像私有的一样。此外,如果接收者是“自己”或“同一类的对象”,也可以由显式接收者(仅)调用受保护的方法。

Private:对于 Ruby 中的私有方法,它永远不能用显式接收器调用。我们可以(仅)使用隐式接收器调用私有方法。这也意味着我们可以从声明它的类以及该类的所有子类中调用私有方法。

于 2017-07-18T03:50:49.267 回答
3

有什么不同?

私有方法解释

@freddie = Person.new
@freddie.hows_it_going?
# => "oh dear, i'm in great pain!"

class Person   
    # public method
    def hows_it_going?
        how_are_your_underpants_feeling?
    end

    private

    def how_are_your_underpants_feeling? # private method
        puts "oh dear, i'm in great pain!"
    end
end

鉴于这是一种公共方法,我们可以询问 Freddie 事情进展如何。这是完全有效的。这是正常的和被接受的。

但是……唯一能知道弗莱迪内裤情况如何的,只有弗莱迪本人。随意的陌生人伸手去摸弗雷迪的内裤并测试情况是不行的——不,不——这是非常非常私密的,我们不想将私密的东西暴露给外界。

@freddie.how_are_your_underpants_feeling?
# => # NoMethodError: private method `how_are_your_underpants_feeling?' called

受保护的方法解释

考虑一下:

class Person
    
    protected

    def gimme_your_credit_card! # protected method
        puts "Fine. Whatever. Here it is: 1234-4567-8910"
    end
end

class Rib < Person
end

class Wife < Rib # wife inherits from Rib
    def i_am_buying_another_handbag_with_your_card(husband)        
        husband.gimme_your_credit_card!
    end
end

@husband = Person.new
@mrs = Wife.new
@mrs.i_am_buying_another_handbag_with_your_card(@husband)
# => puts "Fine. Whatever. Here it is: 1234-4567-8910"

我们对mrs获取我们的信用卡详细信息有些满意,因为mrs我们的肉体是从 Person 继承的,但我们不希望随机的个人访问我们的信用卡详细信息。

如果我们试图在子类之外这样做,它会失败:

@mrs = Wife.new
@mrs.gimme_your_credit_card!
# => protected method gimme_your_credit_card! called for #<Wife:0x00005567b5865818> (NoMethodError)

概括

  • 私有方法只能从内部调用,并且没有“明确的接收者”。(严格来说,您可以使用一点红宝石魔法来访问私有方法,但我暂时将忽略它)。
  • protected 方法可以在子类中调用。
  • 我使用示例/类比来帮助您生动地记住。
于 2021-02-02T23:10:39.577 回答
0

前三种类型的访问说明符以及它们定义了它们的范围。

  1. 公共 -> 访问课堂以外的任何地方。
  2. Private -> 无法在课堂外访问。
  3. 受保护 -> 此方法无法访问此方法定义范围的任何地方。

但是对于如何深入解释所有方法,我有一个解决这个问题的方法。

class Test
  attr_reader :name

  def initialize(name)
    @name = name
  end
  
  def add_two(number)
    @number = number 
  end
  
  def view_address
    address("Anyaddress")
  end
  
  private 

  def address(add)
    @add = add
  end
  
  protected 

  def user_name(name)
    # p 'call method'
    @name = name
  end
end

class Result < Test
  def new_user
    user_name("test355")
  end
end
  1. 对象列表
  2. p test = Test.new("test")
  3. p test.name
  4. p test.add_two(3)
  5. 项目清单
  6. p test.view_address
  7. pr = 结果.new("")
  8. pr.new_user
于 2019-09-19T07:02:30.523 回答