2009-12-07 3 views
4

Я был программистом Java некоторое время, и я пытаюсь переключиться на рубин некоторое время. Я просто пытался разработать небольшую тестовую программу в рубине, и мое намерение - это что-то вроде следующего.Статический блок в Ruby

  1. Я хочу создать простой связанный тип списка объектов в рубине; где переменная экземпляра в классе указывает на другой экземпляр того же типа.
  2. Я хочу заполнить и связать все узлы; перед вызовом конструктора и только один раз. Что-то, что мы обычно делали в Java Static block.

  3. Метод инициализации - это подпись конструктора в рубине. Есть ли какие-то правила вокруг них? Как и в Java, вы не можете вызвать другой конструктор из конструктора, если его не первая строка (или после вызова код класса?)

Спасибо за помощь. -Priyank

+0

Что вы хотите сказать? Часть о правилах инициализации? что ты уже испробовал? С какими проблемами вы столкнулись? –

ответ

19

Я хочу создать простой связанный тип списка объектов в рубине; где переменная экземпляра в классе указывает на другой экземпляр того же типа.

Только быстрое примечание: слово тип очень опасное слово в 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 

для простого связанного списка?

+0

отличный ответ Спасибо! Дал мне много прозрения. – Priyank

+0

Фантастический ответ. Спасибо! – Mike

+0

Спасибо за ясный и полный ответ !!! – shift66

2

Вы можете просто инициализировать переменные класса в классе класса вне любого объявления метода. Он будет вести себя как статический инициализатор в Java:

class Klass 
    @@foo = "bar" 

    def sayFoo 
     puts @@foo 
    end 

    def self.sayFoo 
     puts @@foo 
    end 

end 

Поле класса @@foo здесь инициализируется "bar".

+0

Вопроситель говорил о экземплярах, а не о переменных класса – johannes

+0

Он говорил о * статических блоках * в java (см. Заголовок и пункт 2). Они используются для инициализации переменных класса в java. – paradigmatic

0

В рубина создания объекта работает как этот

class Class 
    def new(*args) 
    obj= self.allocate # get some memory 
    obj.send(:initialize) # call the private method initialize 
    end 
end 
  • Object#initialize просто обычный частный метод.
  • Если вы не хотите, чтобы что-то случилось до Object#initialize, вам нужно написать свой собственный Class#new. Но я не вижу причин, почему вы захотите это сделать.
+0

Я копаю в вопросе кладбища, но должен ли ваш «def new» быть «def Class.new» там? – compman

+1

@compman: Нет. Класс 'Foo' является объектом класса' Class', поэтому, когда вы вызываете 'Foo.new', вы вызываете метод 'Class # new'. –

0

Это в основном тот же ответ paradigmatic вернулся в '09.

Здесь я хочу проиллюстрировать, что «статический инициализатор» может вызывать другой код. Я имитирую сценарий загрузки специального пользователя один раз, после инициализации класса.

class Foo 
    def user 
    "Thomas" 
    end 
end 

class Bar 
    @@my_user = Foo.new.user 

    def my_statically_defined_user 
    @@my_user 
    end 
end 

b = Bar.new 
puts b.my_statically_defined_user # ==> Thomas 
Смежные вопросы