Я хочу создать простой связанный тип списка объектов в рубине; где переменная экземпляра в классе указывает на другой экземпляр того же типа.
Только быстрое примечание: слово тип очень опасное слово в Ruby, особенно если вы из Java. Из-за исторической аварии слово используется как при динамическом наборе текста, так и в статическом печатании, что означает две только поверхностно связанные, но очень разные вещи.
В динамическом вводе, тип представляет собой метка, которая прикрепляется к значению (не ссылки).
Кроме того, в Ruby понятие типа намного шире, чем в Java. В умах программиста Java «тип» означает то же, что и «класс» (хотя это не так, поскольку интерфейсы и примитивы также являются типами). В Ruby «type» означает «что я могу с ним делать».
Пример: в Java, когда я говорю что-то типа Строка, я имею в виду, что это прямой экземпляр класса String
.В Ruby, когда я говорю что-то типа Строка, я имею в виду, что это либо
- прямой экземпляр
String
класса или
- экземпляр подкласса
String
класса или
- в объект, который отвечает на метод
#to_str
или
- объект, который ведет себя неотличимо от строки.
Я хочу заполнить и связать все узлы; перед вызовом конструктора и только один раз. Что-то, что мы обычно делали в Java Static block.
В Ruby все выполнено. В частности, нет такой вещи, как «декларация класса»: тело класса - это просто код, который вызывается, как и любой другой. Если у вас есть список определений методов в вашем классе, это не декларации, которые читаются компилятором, а затем превращаются в объект класса. Это выражения, которые выполняются оценщиком один за другим.
Итак, вы можете поместить любой код, который вам нравится, в тело класса, и этот код будет оцениваться при создании класса. В контексте класса класса self
привязан к классу (помните, что классы - это просто объекты, подобные любым другим).
Метод инициализации - это подпись конструктора в рубине. Есть ли какие-то правила вокруг них? Как и в Java, вы не можете вызвать другой конструктор из конструктора, если его не первая строка (или после вызова код класса?)
Рубин не имеют конструкторами. Конструкторы - это только фабричные методы (с глупыми ограничениями); нет причин иметь их на хорошо продуманном языке, если вы можете просто использовать (более мощный) заводский метод вместо этого.
строительство объектов в Ruby, работает следующим образом: строительство объекта разделяется на две фазы, распределения и инициализации. Выделение осуществляется методом открытого класса, который называется allocate
, который определен как метод экземпляра класса Class
и, как правило, никогда overriden. Он просто выделяет пространство памяти для объекта и устанавливает несколько указателей, однако на данный момент объект на самом деле не используется.
В это место входит инициализатор: это метод экземпляра, который называется initialize
, который устанавливает внутреннее состояние объекта и вводит его в согласованное, полностью определенное состояние, которое может использоваться другими объектами.
Итак, для того, чтобы полностью создать новый объект, то, что вам нужно сделать, это:
x = X.allocate
x.initialize
[Примечание: Objective-C программисты могут признать это.]
Однако, потому что слишком легко забыть позвонить initialize
и, как правило, объект должен быть полностью работоспособен после строительства, есть метод фабрики удобства, называемый Class#new
, который делает все, что сработает для вас и выглядит чем-то как это:
class Class
def new(*args, &block)
obj = alloc
obj.initialize(*args, &block)
return obj
end
end
[Примечание: на самом деле, initialize
является частным, поэтому отражение должен использоваться, чтобы обойти ограничения доступа, как это: obj.send(:initialize, *args, &block)
]
это, кстати, является причиной того, почему в построить объект звоните метод открытого класса Foo.new
, но вы реализуете частный метод экземпляра Foo#initialize
, который, кажется, затмевает много новичков.
Чтобы ответить на ваш вопрос: поскольку метод инициализатора - это всего лишь метод, подобный любому другому, нет абсолютно никаких ограничений относительно того, что вы можете делать с инициализатором, в частности, вы можете звонить super
всякий раз, когда бы то ни было, и как часто вы хотите.
КСТАТИ: с initialize
и new
являются только обычными методами, нет никаких причин, почему они должны называться initialize
и new
. Это только соглашение, хотя и довольно сильное, поскольку оно воплощено в основной библиотеке. В вашем случае вы хотите написать класс коллекции, и для класса коллекции довольно обычным предложить альтернативный заводский метод под названием []
, чтобы я мог позвонить List[1, 2, 3]
вместо List.new(1, 2, 3)
.
Как примечание: одно очевидное преимущество использования обычных методов построения объектов заключается в том, что вы можете создавать экземпляры анонимных классов. Это невозможно в Java, по какой-либо разумной причине. Единственная причина, по которой он не работает, заключается в том, что конструктор имеет то же имя, что и класс, а анонимные классы не имеют имени, ergo не может быть конструктором.
Хотя я не совсем уверен, зачем вам нужно что-то запускать до создания объекта. Если я не имею что-то не хватает, если список в основном быть
class List
def initialize(head=nil, *tail)
@head = head
@tail = List.new(*tail) unless tail.empty?
end
end
для Lisp-стиль конс-лист или
class List
def initialize(*elems)
elems.map! {|el| Element.new(el)}
elems.zip(elems.drop(1)) {|prv, nxt| prv.instance_variable_set(:@next, nxt)}
@head = elems.first
end
class Element
def initialize(this)
@this = this
end
end
end
для простого связанного списка?
Что вы хотите сказать? Часть о правилах инициализации? что ты уже испробовал? С какими проблемами вы столкнулись? –