«супер» и наследование в Ruby

В приведенном ниже дизайне наследования классов класс B наследует класс A и оценивает аргументы своих методов:

class A
  def method_1(arg)
     puts "Using method_1 with argument value '#{arg}'"
  end

  def method_2(arg)
     method_1(arg)
  end
end

class B < A
   def method_1()
      super("foo")
   end

   def method_2()
      super("bar")
   end
end

Вот что я получаю при попытке:

inst_A = A.new
inst_A.method_1("foo")
# >> Using method_1 with argument value 'foo'
inst_A.method_2("bar")
# >> Using method_1 with argument value 'bar'

Я больше не понимаю этого:

inst_B = B.new
inst_B.method_1
# >> Using method_1 with argument value 'foo'
inst_B.method_2
# >> Error: #<ArgumentError: wrong number of arguments (1 for 0)>
# >> <main>:11:in `method_1'
# >> <main>:6:in `method_2'
# >> <main>:16:in `method_2'

Почему при вызове B#method_2 вызывается B#method_1, а не A#method_1?


person Fabien Teulieres    schedule 08.05.2018    source источник
comment
Я предполагаю, что вы получаете сообщение об ошибке, потому что Ruby не поддерживает перегрузку методов? Когда вы наследуете B ‹ A, вы передаете все общедоступные и защищенные методы классу B, поскольку оба имеют одинаковое имя метода, но разное количество аргументов, мне кажется, что вы перегружаете класс B.   -  person Pedro Gabriel Lima    schedule 08.05.2018
comment
Это проще, если вы думаете с точки зрения отправки сообщений. method_1(arg) отправляет сообщение method_1 self, т.е. inst_B. Затем Ruby ищет метод, соответствующий этому имени, начиная с методов B, и находит B#method_1.   -  person Stefan    schedule 08.05.2018


Ответы (2)


Я изменил ваш пример, чтобы распечатать текущий класс в A#method_1

def method_1(arg)
   puts "Using method_1 from class: '#{self.class}' with argument value '#{arg}'"
end

Если вы вызовете B#method_1, вы получите этот вывод

Using method_1 from class: 'B' with argument value 'foo'

Как вы сказали, он вызывает B#method_1 (который переопределяет A#method_1). То же самое относится к ситуации, когда B#method_2 вызывает super, а затем пытается вызвать self#method_1, который не принимает аргументов. self в данном случае имеет тип B, а B переопределяет method_1, чтобы не принимать аргументов.

Ruby сначала пытается найти метод в self и вызывает его, если он найден, в противном случае он просматривает ancestors этого объекта и вызывает первую версию найденного метода. В вашем случае self имеет method_1, который не принимает аргументов, и помните, что Ruby не поддерживает перегрузку методов (если только вы не используете необязательные параметры).

person Omar Bahareth    schedule 08.05.2018
comment
Помните, что Ruby не поддерживает перегрузку методов (если вы не используете необязательные параметры). Я так наивно предполагал, и это было источником всех моих бед. Спасибо! - person Fabien Teulieres; 22.05.2018

method_2 в классе B вызывает method_2(super) в классе A с аргументом "bar". method_2 в классе A (вызывается из класса B) вызывает method_1 в классе B с аргументом "bar". method_1 и method_2 из класса B переопределяют своих однофамильцев из класса A. Вот почему вызывается method_1 из наследующего класса.

person mbuechmann    schedule 08.05.2018
comment
OP знает, что B#method_1 вызывается. Вопрос почему?. - person Stefan; 08.05.2018