class << self
- это больше, чем просто способ объявления методов класса (хотя его можно использовать и таким образом). Возможно, вы видели какое-то использование вроде:
class Foo
class << self
def a
print "I could also have been defined as def Foo.a."
end
end
end
Это работает и эквивалентно def Foo.a
, но способ, которым он работает, немного неуловим. Секрет в том, что self
в этом контексте относится к объекту Foo
, класс которого является уникальным анонимным подклассом Class
. Этот подкласс называется Foo
s eigenclass . Так def a
создает новый метод с именем a
в собственном классе Foo
, доступный с помощью обычного синтаксиса вызова метода: Foo.a
.
Теперь давайте рассмотрим другой пример:
str = "abc"
other_str = "def"
class << str
def frob
return self + "d"
end
end
print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str
Этот пример такой же, как и предыдущий, хотя поначалу будет сложно сказать. frob
определяется не для класса String
, а для собственного класса str
, уникального анонимного подкласса String
. Таким образом, str
имеет метод frob
, но экземпляры String
, как правило, не имеют. Мы могли бы также переопределить методы String (очень полезно в некоторых сложных тестовых сценариях).
Теперь мы готовы понять ваш оригинальный пример. Внутри метода инициализации Foo
, self
относится не к классу Foo
, а к некоторому конкретному экземпляру из Foo
. Его собственный класс является подклассом Foo
, но это не Foo
; это не могло быть, или иначе уловка, которую мы видели во втором примере, не могла работать. Итак, чтобы продолжить ваш пример:
f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)
f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.
Надеюсь, это поможет.