Ruby attr_reader позволяет изменять строковую переменную при использовании <<
Наткнулся на какое-то странное поведение и подумал, может ли кто-нибудь еще подтвердить то, что я вижу.
Предположим, вы создаете класс с переменной-членом и позволяете читать его с помощью attr_reader.
class TestClass
attr_reader :val
def initialize(value)
@val = value
end
end
Теперь, когда я делаю следующее, кажется, что оно изменяет значение @val, хотя я только предоставил ему права на чтение.
test = TestClass.new('hello')
puts test.val
test.val << ' world'
puts test.val
Это возвращает
hello
hello world
Это просто результат некоторого тестирования, которое я провел в irb, поэтому не уверен, что это всегда так
4 ответа
Вы на самом деле не пишете атрибут val. Вы читаете его и вызываете для него метод (метод "<<").
Если вам нужен метод доступа, который предотвращает описанную вами модификацию, вы можете захотеть реализовать метод, который возвращает копию @val, вместо использования attr_reader.
Небольшая модификация вашего примера:
test = TestClass.new([])
Теперь вы должны получить (замените put на p, чтобы получить внутреннее представление):
[]
['hello']
Это то же самое. Вы "читаете" val, и теперь вы можете что-то с этим сделать. В моем примере вы добавляете что-то в массив, в своем примере вы добавляете что-то в свою строку.
Доступ для чтения читает объект (который может быть изменен), доступ для записи изменяет атрибут (он заменяет его).
Возможно, вы ищете freeze
:
class TestClass
attr_reader :val
def initialize(value)
@val = value
@val.freeze
end
end
test = TestClass.new('hello')
puts test.val
test.val << ' world'
puts test.val
Это заканчивается в:
__temp.rb:12:in `<main>': can't modify frozen string (RuntimeError)
hello
Назначение отличается от модификации, а переменные отличаются от объектов.
test.val = "hello world"
будет случай присвоения @val
переменная экземпляра (которая не будет работать), тогда как
test.val << " world"
будет модификация объекта, на который ссылается @val
,
Почему отсутствие оператора присваивания позволяет мне изменять константу Ruby без предупреждения компилятора? это похожий вопрос, но речь идет о константах, а не переменных экземпляра.
Хотя это кажется неожиданным, это совершенно верно. Позволь мне объяснить.
attr_reader
а также attr_writer
методы макроса класса определяют методы "getter" и "setter" для переменных экземпляра.
Без метода "getter" у вас нет доступа к переменным экземпляра объекта просто потому, что вы не находитесь в контексте этого объекта. Метод "setter" по сути такой:
def variable=(value)
@variable = value
end
Поскольку переменная экземпляра указывает на изменяемый объект с самим набором методов, если вы "получаете" его и манипулируете им, само собой разумеется, что эти изменения будут приняты. Вам не нужно использовать вышеуказанный метод установки для вызова variable.<<(value)
,