我可以从父类调用子类的私有方法吗?

use*_*934 1 ruby inheritance private subclass superclass

在阅读了 Ruby 的访问控制之后,我了解到私有方法只能从类内部以及该类的子类中隐式调用。不过,我有一个例子,其中一个类似乎default_chain在其子类上调用私有方法,并且它仍然有效。查看以下代码(改编自 Sandi Metz 的实用 Ruby 面向对象设计):

class Bicycle
    attr_reader :chain

    def initialize(args={})
        @chain = args[:chain] || default_chain
    end

    def parts
        {
            chain: chain
        }
    end
end

class RoadBike < Bicycle
    def parts
        super.merge(
            handlebar_tape_color: "red"
        )
    end

    private

    def default_chain
        "21-speed"
    end
end

class MountainBike < Bicycle
    def parts
        super.merge(
            suspension: "Manitou Mezzer Pro"
        )
    end

    private

    def default_chain
        "10-speed"
    end
end


RoadBike.new.parts  # {:chain=>"21-speed", :handlebar_tape_color=>"red"}
MountainBike.new.parts  # {:chain=>"10-speed", :suspension=>"Manitou Mezzer Pro"}
Run Code Online (Sandbox Code Playgroud)

这是怎么回事?

Kon*_*kov 5

您弄错了 - 在您的示例中,不存在父类调用子方法之类的事情。

Ruby 中的方法/常量名称查找总是“自下而上”进行:首先我们检查该方法是否在对象的类中定义,然后在对象的类的超类中定义,依此类推(这是一个巨大的简化,因为 Ruby 的对象模型更复杂,更多这个稍后再说)。因此,在您的示例中,事情大致按以下顺序发生:

  1. 当您调用RoadBike.new运行时时,检查是否有为initializeRoadBike 类定义的方法。没有,所以我们使用为其父类定义的实现 - Bycicle(但执行上下文保持不变 - 它仍然是RoadBike实例)

  2. 当执行Bycicle#initialize运行时遇到另一个方法调用时 - default_chain. 此时,我们以完全相同的方式开始方法名称解析 - 从 RoadBike 上下文开始。RoadBike有自己的实现吗default_chain?是的,确实如此,所以我们简单地称呼它。

希望下面的婴儿示例能够清楚地说明这一点:

class Parent
  def initialize
    puts "Parent Initializer is called"
    a
    b
  end

  def a
    puts "Parent a is called"
  end

  def b
    puts "Parent b is called"
  end
end

class Child < Parent
  def b
    puts "Child b is called"
  end
end

pry(main)> Child.new
Parent Initializer is called
Parent a is called
Child b is called
Run Code Online (Sandbox Code Playgroud)

实际上,方法/常量解析机制更为复杂(包括所谓的单例类)。这是一个更大的主题,不太适合 SO 答案,所以我强烈建议阅读 Paolo Perotta 的“ Metaprogramming Ruby 2 ”,其中从非常实用的角度对该模型进行了很好的详细解释。