#ruby #access-modifiers
Вопрос:
Я пытаюсь запустить этот код, и он выдает разные результаты с разными версиями ruby 2.5 — 2.7
код:
class ParentClass
def the_public_method
self.method1
end
private
def method1
puts "The private has been called"
end
end
class ChildClass < ParentClass
def test
self.method1
end
end
ParentClass.new.the_public_method
ChildClass.new.test
на ruby 2.5 это дает:
Traceback (most recent call last):
1: from main.rb:19:in `<main>'
main.rb:3:in `the_public_method': private method `method1' called for
#<ParentClass:0x000056367ee0b388> (NoMethodError)
Did you mean? method
methods
exit status 1
на ruby 2.7 это дает:
The private has been called
The private has been called
Я думаю, что первый вывод верен для более старой версии ruby..
есть какие-нибудь отзывы?
Ответ №1:
Я думаю, что первый вывод верен для более старой версии ruby.. есть какие-нибудь отзывы?
Оба вывода верны. Спецификация была изменена в Ruby 2.7, поэтому, естественно, Ruby 2.7 ведет себя по-другому.
Первоначально правилом для частных методов было «частные методы могут вызываться только без явного получателя».
Однако это означает, что вы не можете использовать частные установщики, поскольку foo = :bar
это назначение локальной переменной и self.foo = :bar
не разрешено.
Таким образом, правило было изменено на «частные методы могут вызываться только без явного получателя, за исключением установщиков, где self
в качестве получателя допускается литеральная псевдопеременная».
Но это все еще не учитывает такие вещи, как self 2
или self.foo = 2
где
foo
, или foo=
являются частными , и многие, многие другие угловые случаи.
Некоторое время разработчики Ruby пытались справиться с этим, либо игнорируя некоторые из этих угловых случаев, либо добавляя все более сложный набор исключений, но на самом деле решение довольно простое: измените правило на «частные методы можно вызывать только с буквальной псевдопеременной self
в качестве явного или неявного получателя».
И это правило действует со времен Ruby 2.7.
Комментарии:
1. К сожалению, угловой случай
obj = self ; obj.foo
все еще терпит неудачу.2. Одна вещь, которая объединяет все эти определения, заключается в том, что их можно синтаксически проверить. Несколько разработчиков (я лично знаю о JRuby и TruffleRuby) очень хотели сохранить это свойство, что было верно с тех пор, как в Ruby существовали частные методы. Всегда требовалось, чтобы только литеральная псевдопеременная
self
могла использоваться в качестве явного приемника для частных методов. В противном случае вы столкнетесь с такими проблемами, какdef foo; if rand < 0.5 then self else Object.new end end; foo.bar
: теперь , еслиbar
это личное, разрешено это или нет?3. Да, тогда вам придется проверить вызов. Но я не понимаю, почему это плохо.
Ответ №2:
Ruby 2.7 позволяет вызывать частный метод с помощью self
До Ruby 2.7 вызов частного метода записи/назначения с буквальным self в качестве получателя был разрешен, но вызов любого другого частного метода с self приводил к ошибке nometoderror.
Ruby 2.7 нацелен на стандартизацию взаимодействия между собственными и частными методами. Вышеуказанное несоответствие было исправлено в Ruby 2.7.
Таким образом, первый вывод верен до Ruby 2.7, второй вывод верен после Ruby 2.7.
Ответ №3:
В Ruby private
метод по-прежнему доступен из унаследованных классов, но используется для требования неявного полученного (т. Е. неявного вызова, например mehtod1
, но не obj.method1
или self.method1
)
Как заявил @eux, это последнее требование было смягчено в ruby 2.7, так что теперь вы self.method
тоже можете звонить.
Еще одна особенность видимости в Ruby заключается в том, что она связана с экземплярами, а не с самим классом. Это объясняет поведение private
и позволяет вам понять следующий код:
class Foo
def initialize(name)
@name = name
end
def ==(rhs)
name == rhs.name
end
private
attr_reader :name
end
f = Foo.new("bar")
f == f # NoMethodError
Здесь возникает ошибка nometoderror, потому attr_reader :name
что она закрыта, поэтому вы не можете получить доступ к методу name
другого объекта. Чтобы включить такое поведение, используйте protected